Browse Source

Merge branch 'main' into CLDC-1464-completion-date-validations

pull/1232/head
natdeanlewissoftwire 3 years ago committed by GitHub
parent
commit
31acaa63a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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. 18
      app/models/form/sales/pages/grant_value_check.rb
  6. 27
      app/models/form/sales/pages/shared_ownership_deposit_value_check.rb
  7. 1
      app/models/form/sales/questions/grant.rb
  8. 23
      app/models/form/sales/questions/grant_value_check.rb
  9. 1
      app/models/form/sales/questions/mortgage_length.rb
  10. 23
      app/models/form/sales/questions/shared_ownership_deposit_value_check.rb
  11. 1
      app/models/form/sales/subsections/discounted_ownership_scheme.rb
  12. 5
      app/models/form/sales/subsections/shared_ownership_scheme.rb
  13. 4
      app/models/log.rb
  14. 10
      app/models/sales_log.rb
  15. 4
      app/models/user.rb
  16. 10
      app/models/validations/sales/financial_validations.rb
  17. 25
      app/models/validations/sales/sale_information_validations.rb
  18. 16
      app/models/validations/sales/soft_validations.rb
  19. 1
      app/services/bulk_upload/lettings/validator.rb
  20. 11
      app/views/bulk_upload_lettings_results/resume.html.erb
  21. 54
      app/views/logs/_log_filters.erb
  22. 4
      app/views/logs/_log_list.html.erb
  23. 62
      app/views/logs/index.html.erb
  24. 8
      config/locales/en.yml
  25. 6
      config/routes.rb
  26. 7
      db/migrate/20230123101256_add_shared_ownership_deposit_value_check.rb
  27. 5
      db/migrate/20230123160741_add_grant_value_check_to_sales_log.rb
  28. 5
      db/migrate/20230126145529_add_column_to_bulk_upload_errors.rb
  29. 8
      db/schema.rb
  30. 68
      spec/controllers/bulk_upload_lettings_results_controller_spec.rb
  31. 4
      spec/factories/sales_log.rb
  32. 4
      spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
  33. 4
      spec/models/form/sales/pages/deposit_value_check_spec.rb
  34. 4
      spec/models/form/sales/pages/handover_date_check_spec.rb
  35. 4
      spec/models/form/sales/pages/household_wheelchair_check_spec.rb
  36. 4
      spec/models/form/sales/pages/mortgage_value_check_spec.rb
  37. 4
      spec/models/form/sales/pages/retirement_value_check_spec.rb
  38. 4
      spec/models/form/sales/pages/savings_value_check_spec.rb
  39. 50
      spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb
  40. 4
      spec/models/form/sales/questions/mortgage_length_spec.rb
  41. 61
      spec/models/form/sales/questions/shared_ownership_deposit_value_check_spec.rb
  42. 1
      spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb
  43. 3
      spec/models/form/sales/subsections/shared_ownership_scheme_spec.rb
  44. 4
      spec/models/form_handler_spec.rb
  45. 8
      spec/models/sales_log_spec.rb
  46. 6
      spec/models/user_spec.rb
  47. 26
      spec/models/validations/sales/financial_validations_spec.rb
  48. 210
      spec/models/validations/sales/sale_information_validations_spec.rb
  49. 124
      spec/models/validations/sales/soft_validations_spec.rb
  50. 104
      spec/requests/lettings_logs_controller_spec.rb
  51. 5
      spec/requests/organisations_controller_spec.rb
  52. 9
      spec/services/bulk_upload/lettings/validator_spec.rb
  53. 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 def show
@bulk_upload = current_user.bulk_uploads.lettings.find(params[:id]) @bulk_upload = current_user.bulk_uploads.lettings.find(params[:id])
end 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 end

14
app/controllers/lettings_logs_controller.rb

@ -3,6 +3,9 @@ class LettingsLogsController < LogsController
before_action :session_filters, if: :current_user before_action :session_filters, if: :current_user
before_action :set_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 def index
respond_to do |format| respond_to do |format|
format.html do format.html do
@ -109,6 +112,17 @@ class LettingsLogsController < LogsController
private 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 def permitted_log_params
params.require(:lettings_log).permit(LettingsLog.editable_fields) params.require(:lettings_log).permit(LettingsLog.editable_fields)
end end

4
app/controllers/modules/logs_filter.rb

@ -7,7 +7,9 @@ module Modules::LogsFilter
def load_session_filters(specific_org: false) def load_session_filters(specific_org: false)
current_filters = session[:logs_filters] current_filters = session[:logs_filters]
new_filters = current_filters.present? ? JSON.parse(current_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 params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
end end

5
app/helpers/logs_helper.rb

@ -18,4 +18,9 @@ module LogsHelper
bulk_upload_sales_log_path(id:) bulk_upload_sales_log_path(id:)
end end
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 end

18
app/models/form/sales/pages/grant_value_check.rb

@ -0,0 +1,18 @@
class Form::Sales::Pages::GrantValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "grant_value_check"
@depends_on = [
{
"grant_outside_common_range?" => true,
},
]
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::GrantValueCheck.new(nil, nil, self),
]
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/grant.rb

@ -6,6 +6,7 @@ class Form::Sales::Questions::Grant < ::Form::Question
@header = "What was the amount of any loan, grant, discount or subsidy given?" @header = "What was the amount of any loan, grant, discount or subsidy given?"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 999_999
@width = 5 @width = 5
@prefix = "£" @prefix = "£"
@hint_text = "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB)" @hint_text = "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB)"

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

@ -0,0 +1,23 @@
class Form::Sales::Questions::GrantValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "grant_value_check"
@check_answer_label = "Grant value confirmation"
@header = "Are you sure? Grants are usually £9,000 - £16,000"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"grant_value_check" => 0,
},
{
"grant_value_check" => 1,
},
],
}
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?" @header = "What is the length of the mortgage?"
@type = "numeric" @type = "numeric"
@min = 0 @min = 0
@max = 60
@width = 5 @width = 5
@suffix = " years" @suffix = " years"
@hint_text = "You should round up to the nearest year. Value should not exceed 60 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

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

@ -12,6 +12,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::AboutPriceRtb.new(nil, nil, self), Form::Sales::Pages::AboutPriceRtb.new(nil, nil, self),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_price_value_check", nil, self), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_price_value_check", nil, self),
Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self), Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self),
Form::Sales::Pages::GrantValueCheck.new(nil, nil, self),
Form::Sales::Pages::PurchasePrice.new("purchase_price_discounted_ownership", nil, self), Form::Sales::Pages::PurchasePrice.new("purchase_price_discounted_ownership", nil, self),
Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self), Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self), Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self),

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

@ -21,15 +21,18 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection
Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self), Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self),
Form::Sales::Pages::PreviousTenure.new(nil, nil, self), Form::Sales::Pages::PreviousTenure.new(nil, nil, self),
Form::Sales::Pages::AboutPriceSharedOwnership.new(nil, nil, self), Form::Sales::Pages::AboutPriceSharedOwnership.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::Mortgageused.new("mortgage_used_shared_ownership", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_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::MortgageLender.new("mortgage_lender_shared_ownership", nil, self),
Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_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::MortgageLength.new("mortgage_length_shared_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_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::AboutDepositWithDiscount.new(nil, nil, self),
Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", 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::MonthlyRent.new(nil, nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", 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) where(created_by: user)
end 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) } scope :created_by, ->(user) { where(created_by: user) }
def collection_start_year def collection_start_year

10
app/models/sales_log.rb

@ -160,6 +160,12 @@ class SalesLog < Log
end end
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) def process_postcode(postcode, postcode_known_key, la_inferred_key, la_key)
return if postcode.blank? return if postcode.blank?
@ -212,4 +218,8 @@ class SalesLog < Log
def old_persons_shared_ownership? def old_persons_shared_ownership?
type == 24 type == 24
end end
def shared_owhership_scheme?
ownershipsch == 1
end
end end

4
app/models/user.rb

@ -145,9 +145,9 @@ class User < ApplicationRecord
def logs_filters(specific_org: false) def logs_filters(specific_org: false)
if (support? && !specific_org) || organisation.has_managing_agents? if (support? && !specific_org) || organisation.has_managing_agents?
%w[status years user organisation] %w[status years user organisation bulk_upload_id]
else else
%w[status years user] %w[status years user bulk_upload_id]
end end
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 # or 'validate_' to run on submit as well
def validate_income1(record) 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? 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 :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 elsif record.income1 > 80_000
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 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 end
end end

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

@ -42,4 +42,29 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :fromprop, I18n.t("validations.sale_information.previous_property_type.property_type_bedsit") record.errors.add :fromprop, I18n.t("validations.sale_information.previous_property_type.property_type_bedsit")
end end
end end
def validate_discounted_ownership_value(record)
return unless record.value && record.deposit && record.ownershipsch
return unless record.mortgage || record.mortgageused == 2
return unless record.discount || record.grant || record.type == 29
discount_amount = record.discount ? record.value * record.discount / 100 : 0
grant_amount = record.grant || 0
mortgage_amount = record.mortgage || 0
value_with_discount = (record.value - discount_amount)
if mortgage_amount + record.deposit + grant_amount != value_with_discount && record.discounted_ownership_sale?
%i[mortgage deposit grant value discount ownershipsch].each do |field|
record.errors.add field, I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: sprintf("%.2f", value_with_discount))
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 end

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

@ -43,9 +43,25 @@ module Validations::Sales::SoftValidations
extrabor != 1 && mortgage + deposit > value - value * discount / 100 extrabor != 1 && mortgage + deposit > value - value * discount / 100
end 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? def hodate_3_years_or_more_saledate?
return unless hodate && saledate return unless hodate && saledate
((saledate.to_date - hodate.to_date).to_i / 365) >= 3 ((saledate.to_date - hodate.to_date).to_i / 365) >= 3
end end
def grant_outside_common_range?
return unless grant
!grant.between?(9_000, 16_000)
end
end end

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

@ -164,6 +164,7 @@ class BulkUpload::Lettings::Validator
property_ref: row_parser.field_100, property_ref: row_parser.field_100,
row:, row:,
cell: "#{cols[field_number_for_attribute(error.attribute) - col_offset + 1]}#{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
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"> <div class="app-filter__header">
<h2 class="govuk-heading-m">Filters</h2> <h2 class="govuk-heading-m">Filters</h2>
</div> </div>
<div class="app-filter__content"> <div class="app-filter__content">
<%= form_with html: { method: :get } do |f| %> <%= form_with html: { method: :get } do |f| %>
<% years = {"2021": "2021/22", "2022": "2022/23"} %> <% years = { "2021": "2021/22", "2022": "2022/23" } %>
<% all_or_yours = {"all": { label: "All" }, "yours": { label: "Yours" } } %> <% 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" } %> <% if bulk_upload_options(@bulk_upload).present? %>
<%= render partial: "filters/radio_filter", locals: { f: f, options: all_or_yours, label: "Logs", category: "user", } %> <%= 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" %> <% if (@current_user.support? || @current_user.organisation.has_managing_agents?) && request.path == "/lettings-logs" %>
<%= render partial: "filters/radio_filter", locals: { <%= render partial: "filters/radio_filter", locals: {
f: f, f: f,
@ -21,14 +56,15 @@
type: "select", type: "select",
label: "Organisation", label: "Organisation",
category: "organisation", category: "organisation",
options: organisations_filter_options(@current_user) options: organisations_filter_options(@current_user),
} },
} },
}, },
label: "Organisation", label: "Organisation",
category: "organisation_select" category: "organisation_select",
} %> } %>
<% end %> <% end %>
<%= f.govuk_submit "Apply filters", class: "govuk-!-margin-bottom-0" %> <%= f.govuk_submit "Apply filters", class: "govuk-!-margin-bottom-0" %>
<% end %> <% end %>
</div> </div>

4
app/views/logs/_log_list.html.erb

@ -1,6 +1,8 @@
<h2 class="govuk-body"> <h2 class="govuk-body">
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv" %> <% if logs&.first&.lettings? %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv" %>
<% end %>
</h2> </h2>
<% logs.map do |log| %> <% logs.map do |log| %>
<%= render(LogSummaryComponent.new(current_user:, log:)) %> <%= render(LogSummaryComponent.new(current_user:, log:)) %>

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

@ -11,32 +11,58 @@
title_id: "impacted-logs-banner", title_id: "impacted-logs-banner",
) do |notification_banner| %> ) do |notification_banner| %>
<% notification_banner.heading(text: "A scheme has changed and it has affected #{@unresolved_count} #{'log'.pluralize(@unresolved_count)}") %> <% notification_banner.heading(text: "A scheme has changed and it has affected #{@unresolved_count} #{'log'.pluralize(@unresolved_count)}") %>
<div class="govuk-notification-banner__heading"> <div class="govuk-notification-banner__heading">
<%= govuk_link_to "Update logs", update_logs_lettings_logs_path, class: "govuk-notification-banner__link" %> <%= govuk_link_to "Update logs", update_logs_lettings_logs_path, class: "govuk-notification-banner__link" %>
</div> </div>
<% end %>
<% end %> <% end %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Lettings logs", sub: nil } : { main: "Lettings logs", sub: current_user.organisation.name } %> <% end %>
<% 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"> <% if @bulk_upload.blank? %>
<div class="govuk-button-group app-filter-toggle govuk-!-margin-bottom-6"> <%= 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 } %>
<% if current_page?(controller: 'lettings_logs', action: 'index') %> <% else %>
<%= govuk_button_to "Create a new lettings log", lettings_logs_path, class: "govuk-!-margin-right-6" %> <%= render partial: "organisations/headings",
<% end %> 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') %> <div class="app-card govuk-!-margin-bottom-9">
<%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %> <div class="govuk-grid-row">
<% end %> <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? %> <p class="govuk-body">
<%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %> <strong>Bulk Upload details:</strong><br>
<% end %> <%= @bulk_upload.filename %><br>
Uploaded on <%= @bulk_upload.created_at.to_fs(:govuk_date_and_time) %><br>
</p>
</div>
</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" %> <%= render partial: "log_filters" %>
<div class="app-filter-layout__content"> <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) %> <%= 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") %> <%= govuk_section_break(visible: true, size: "m") %>

8
config/locales/en.yml

@ -428,8 +428,10 @@ en:
must_be_less_than_1_year_from_exdate: "Completion date must be less than 1 year after contract exchange date" must_be_less_than_1_year_from_exdate: "Completion date must be less than 1 year after contract exchange date"
previous_property_beds: previous_property_beds:
property_type_bedsit: "Bedsit bedroom maximum 1" property_type_bedsit: "Bedsit bedroom maximum 1"
previous_property_type: discounted_ownership_value: "Mortgage, deposit, and grant total must equal £%{value_with_discount}"
property_type_bedsit: "A bedsit can not have more than 1 bedroom" monthly_rent:
higher_than_expected: "Basic monthly rent must be between £0 and £9,999"
soft_validations: soft_validations:
net_income: net_income:
title_text: "Net income is outside the expected range based on the lead tenant’s working situation" title_text: "Net income is outside the expected range based on the lead tenant’s working situation"
@ -462,6 +464,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" 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: void_date:
title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years" 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: devise:
two_factor_authentication: two_factor_authentication:

6
config/routes.rb

@ -134,7 +134,11 @@ Rails.application.routes.draw do
end end
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" get "update-logs", to: "lettings_logs#update_logs"
end 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/20230123160741_add_grant_value_check_to_sales_log.rb

@ -0,0 +1,5 @@
class AddGrantValueCheckToSalesLog < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :grant_value_check, :integer
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

8
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_01_18_170602) 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 # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -25,6 +25,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_18_170602) do
t.text "error" t.text "error"
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.text "col"
t.index ["bulk_upload_id"], name: "index_bulk_upload_errors_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_bulk_upload_errors_on_bulk_upload_id"
end end
@ -486,9 +487,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_18_170602) do
t.integer "hoyear" t.integer "hoyear"
t.integer "fromprop" t.integer "fromprop"
t.integer "socprevten" t.integer "socprevten"
t.integer "mortlen"
t.integer "mortgagelender" t.integer "mortgagelender"
t.string "mortgagelenderother" t.string "mortgagelenderother"
t.integer "mortlen"
t.integer "extrabor" t.integer "extrabor"
t.integer "hhmemb" t.integer "hhmemb"
t.integer "totadult" t.integer "totadult"
@ -501,9 +502,12 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_18_170602) do
t.boolean "is_la_inferred" t.boolean "is_la_inferred"
t.bigint "bulk_upload_id" t.bigint "bulk_upload_id"
t.integer "retirement_value_check" t.integer "retirement_value_check"
t.integer "deposit_and_mortgage_value_check"
t.integer "grant_value_check"
t.integer "hodate_check" t.integer "hodate_check"
t.integer "extrabor_value_check" t.integer "extrabor_value_check"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.integer "shared_ownership_deposit_value_check"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_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" 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/factories/sales_log.rb

@ -70,10 +70,10 @@ FactoryBot.define do
ecstat5 { 2 } ecstat5 { 2 }
ecstat6 { 1 } ecstat6 { 1 }
disabled { 1 } disabled { 1 }
deposit { 10_000 } deposit { 80_000 }
cashdis { 1_000 } cashdis { 1_000 }
value { 110_000 } value { 110_000 }
grant { 1_000 } grant { 10_000 }
proplen { 10 } proplen { 10 }
pregyrha { 1 } pregyrha { 1 }
pregla { 1 } pregla { 1 }

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 end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 expect(page.header).to be_nil
end end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
it "has correct depends_on" do it "has correct depends_on" do
expect(page.depends_on).to eq([ 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 end
end end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 end
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
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 it "has correct min" do
expect(question.min).to eq(0) expect(question.min).to eq(0)
end end
it "has correct max" do
expect(question.max).to eq(60)
end
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

1
spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb

@ -18,6 +18,7 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model
about_price_rtb about_price_rtb
extra_borrowing_price_value_check extra_borrowing_price_value_check
about_price_not_rtb about_price_not_rtb
grant_value_check
purchase_price_discounted_ownership purchase_price_discounted_ownership
mortgage_used_discounted_ownership mortgage_used_discounted_ownership
mortgage_amount_discounted_ownership mortgage_amount_discounted_ownership

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

@ -27,14 +27,17 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipScheme, type: :model do
previous_property_type previous_property_type
shared_ownership_previous_tenure shared_ownership_previous_tenure
about_price_shared_ownership about_price_shared_ownership
shared_ownership_equity_value_check
mortgage_used_shared_ownership mortgage_used_shared_ownership
mortgage_amount_shared_ownership mortgage_amount_shared_ownership
shared_ownership_mortgage_amount_value_check
mortgage_lender_shared_ownership mortgage_lender_shared_ownership
mortgage_lender_other_shared_ownership mortgage_lender_other_shared_ownership
mortgage_length_shared_ownership mortgage_length_shared_ownership
extra_borrowing_shared_ownership extra_borrowing_shared_ownership
about_deposit_with_discount about_deposit_with_discount
about_deposit_shared_ownership about_deposit_shared_ownership
deposit_value_check
shared_ownership_deposit_value_check shared_ownership_deposit_value_check
monthly_rent monthly_rent
leasehold_charges_shared_ownership 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 it "is able to load a current sales form" do
form = form_handler.get_form("current_sales") form = form_handler.get_form("current_sales")
expect(form).to be_a(Form) expect(form).to be_a(Form)
expect(form.pages.count).to eq(187) expect(form.pages.count).to eq(191)
expect(form.name).to eq("2022_2023_sales") expect(form.name).to eq("2022_2023_sales")
end end
it "is able to load a previous sales form" do it "is able to load a previous sales form" do
form = form_handler.get_form("previous_sales") form = form_handler.get_form("previous_sales")
expect(form).to be_a(Form) expect(form).to be_a(Form)
expect(form.pages.count).to eq(187) expect(form.pages.count).to eq(191)
expect(form.name).to eq("2021_2022_sales") expect(form.name).to eq("2021_2022_sales")
end end
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) expect(record_from_db["prevloc"]).to eq(nil)
end end
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 end

6
spec/models/user_spec.rb

@ -123,7 +123,7 @@ RSpec.describe User, type: :model do
end end
it "can filter lettings logs by user, year and status" do 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
end end
@ -133,7 +133,7 @@ RSpec.describe User, type: :model do
end end
it "can filter lettings logs by user, year, status and organisation" do 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 end
end end
@ -159,7 +159,7 @@ RSpec.describe User, type: :model do
end end
it "can filter lettings logs by user, year, status and organisation" do 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 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 } } let(:validator_class) { Class.new { include Validations::Sales::FinancialValidations } }
describe "income validations" do 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 "with shared ownership" do
context "and non london borough" do context "and non london borough" do
@ -17,6 +17,14 @@ RSpec.describe Validations::Sales::FinancialValidations do
financial_validator.validate_income1(record) financial_validator.validate_income1(record)
expect(record.errors["income1"]) expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)) .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
end end
@ -25,6 +33,10 @@ RSpec.describe Validations::Sales::FinancialValidations do
record.ecstat1 = 1 record.ecstat1 = 1
financial_validator.validate_income1(record) financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty 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 end
@ -41,6 +53,14 @@ RSpec.describe Validations::Sales::FinancialValidations do
financial_validator.validate_income1(record) financial_validator.validate_income1(record)
expect(record.errors["income1"]) expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000)) .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
end end
@ -49,6 +69,10 @@ RSpec.describe Validations::Sales::FinancialValidations do
record.ecstat1 = 1 record.ecstat1 = 1
financial_validator.validate_income1(record) financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty 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 end
end end

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

@ -224,4 +224,214 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end end
end end
end end
describe "#validate_discounted_ownership_value" do
context "when grant is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 8) }
context "and not provided" do
before do
record.grant = nil
end
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "and is provided" do
it "adds an error if mortgage, deposit and grant total does not equal market value" do
record.grant = 3_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
end
it "does not add an error if mortgage, deposit and grant total equals market value" do
record.grant = 15_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
end
context "when discount is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, value: 30_000, ownershipsch: 2, type: 9) }
context "and not provided" do
before do
record.discount = nil
end
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "and is provided" do
it "adds an error if mortgage and deposit total does not equal market value - discount" do
record.discount = 10
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "27000.00"))
end
it "does not add an error if mortgage and deposit total equals market value - discount" do
record.discount = 50
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
end
context "when neither discount nor grant is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, value: 30_000, ownershipsch: 2, type: 29) }
it "adds an error if mortgage and deposit total does not equal market value" do
record.deposit = 2_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "30000.00"))
end
it "does not add an error if mortgage and deposit total equals market value" do
record.deposit = 20_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "when mortgage is routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgageused: 1, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2) }
context "and not provided" do
before do
record.mortgage = nil
end
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "and is provided" do
it "adds an error if mortgage, grant and deposit total does not equal market value - discount" do
record.mortgage = 10
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
end
it "does not add an error if mortgage, grant and deposit total equals market value - discount" do
record.mortgage = 10_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
end
context "when mortgage is not routed to" do
let(:record) { FactoryBot.build(:sales_log, mortgageused: 2, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 2) }
it "adds an error if grant and deposit total does not equal market value - discount" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors[:mortgage]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:deposit]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:grant]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:value]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
expect(record.errors[:discount]).to include(I18n.t("validations.sale_information.discounted_ownership_value", value_with_discount: "18000.00"))
end
it "does not add an error if mortgage, grant and deposit total equals market value - discount" do
record.grant = 13_000
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
end
end
context "when owhership is not discounted" do
let(:record) { FactoryBot.build(:sales_log, mortgage: 10_000, deposit: 5_000, grant: 3_000, value: 20_000, discount: 10, ownershipsch: 1) }
it "does not add an error" do
sale_information_validator.validate_discounted_ownership_value(record)
expect(record.errors).to be_empty
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 end

124
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 .not_to be_deposit_over_soft_max
end end
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 end
describe "hodate_more_than_3_years_before_saledate" do describe "hodate_more_than_3_years_before_saledate" do
@ -396,4 +500,24 @@ RSpec.describe Validations::Sales::SoftValidations do
expect(record).not_to be_wheelchair_when_not_disabled expect(record).not_to be_wheelchair_when_not_disabled
end end
end end
describe "#grant_outside_common_range?" do
it "returns true if grant is below 9000" do
record.grant = 1_000
expect(record).to be_grant_outside_common_range
end
it "returns true if grant is above 16000" do
record.grant = 100_000
expect(record).to be_grant_outside_common_range
end
it "returns false if grant is within expected range" do
record.grant = 10_000
expect(record).not_to be_grant_outside_common_range
end
end
end end

104
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) expect(page).not_to have_link(lettings_log_2022.id.to_s)
end end
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
end end
@ -458,6 +561,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "includes the search on the CSV link" do it "includes the search on the CSV link" do
search_term = "foo" search_term = "foo"
FactoryBot.create(:lettings_log, created_by: user, owning_organisation: user.organisation, tenancycode: "foo")
get "/lettings-logs?search=#{search_term}", headers: headers, params: {} get "/lettings-logs?search=#{search_term}", headers: headers, params: {}
expect(page).to have_link("Download (CSV)", href: "/lettings-logs/csv-download?search=#{search_term}") expect(page).to have_link("Download (CSV)", href: "/lettings-logs/csv-download?search=#{search_term}")
end end

5
spec/requests/organisations_controller_spec.rb

@ -1141,10 +1141,11 @@ RSpec.describe OrganisationsController, type: :request do
context "when they view the logs tab" do context "when they view the logs tab" do
before do before do
FactoryBot.create(:lettings_log, owning_organisation: organisation)
get "/organisations/#{organisation.id}/lettings-logs" get "/organisations/#{organisation.id}/lettings-logs"
end end
it "has a CSV download button with the correct path" do it "has a CSV download button with the correct path if at least 1 log exists" do
expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs/csv-download") expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs/csv-download")
end end
@ -1152,7 +1153,7 @@ RSpec.describe OrganisationsController, type: :request do
let(:other_organisation) { FactoryBot.create(:organisation) } let(:other_organisation) { FactoryBot.create(:organisation) }
before do before do
FactoryBot.create_list(:lettings_log, 3, owning_organisation: organisation) FactoryBot.create_list(:lettings_log, 2, owning_organisation: organisation)
FactoryBot.create_list(:lettings_log, 2, owning_organisation: other_organisation) FactoryBot.create_list(:lettings_log, 2, owning_organisation: other_organisation)
end end

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

@ -43,7 +43,14 @@ RSpec.describe BulkUpload::Lettings::Validator do
validator.call validator.call
error = BulkUploadError.first 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
end end

34
spec/support/bulk_upload/log_to_csv.rb

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

Loading…
Cancel
Save