Browse Source

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

# Conflicts:
#	app/models/validations/sales/sale_information_validations.rb
#	app/models/validations/sales/soft_validations.rb
#	config/locales/en.yml
#	db/schema.rb
#	spec/models/form_handler_spec.rb
#	spec/models/validations/sales/sale_information_validations_spec.rb
#	spec/models/validations/sales/soft_validations_spec.rb
pull/1225/head
natdeanlewissoftwire 2 years ago
parent
commit
2f64c66aa9
  1. 12
      app/components/log_summary_component.html.erb
  2. 6
      app/controllers/lettings_logs_controller.rb
  3. 1
      app/controllers/logs_controller.rb
  4. 7
      app/helpers/tab_nav_helper.rb
  5. 2
      app/models/form.rb
  6. 21
      app/models/form/sales/pages/extra_borrowing_value_check.rb
  7. 18
      app/models/form/sales/pages/grant_value_check.rb
  8. 1
      app/models/form/sales/questions/buyer1_income.rb
  9. 2
      app/models/form/sales/questions/buyer1_income_known.rb
  10. 1
      app/models/form/sales/questions/buyer2_income.rb
  11. 2
      app/models/form/sales/questions/buyer2_income_known.rb
  12. 23
      app/models/form/sales/questions/extra_borrowing_value_check.rb
  13. 1
      app/models/form/sales/questions/grant.rb
  14. 23
      app/models/form/sales/questions/grant_value_check.rb
  15. 1
      app/models/form/sales/questions/mortgage_length.rb
  16. 2
      app/models/form/sales/questions/number_of_others_in_property.rb
  17. 5
      app/models/form/sales/subsections/discounted_ownership_scheme.rb
  18. 14
      app/models/lettings_log.rb
  19. 14
      app/models/log.rb
  20. 1
      app/models/organisation.rb
  21. 9
      app/models/sales_log.rb
  22. 1
      app/models/user.rb
  23. 4
      app/models/validations/sales/household_validations.rb
  24. 39
      app/models/validations/sales/sale_information_validations.rb
  25. 13
      app/models/validations/sales/soft_validations.rb
  26. 10
      app/services/filter_service.rb
  27. 17
      app/views/form/guidance/_what_counts_as_income_sales.html.erb
  28. 2
      app/views/form/page.html.erb
  29. 4
      app/views/logs/_log_list.html.erb
  30. 2
      config/environments/development.rb
  31. 2
      config/environments/test.rb
  32. 11
      config/locales/en.yml
  33. 15450
      config/rent_range_data/2022.csv
  34. 13
      db/migrate/20230110094518_remove_managing_organisation_id_from_sales_logs.rb
  35. 7
      db/migrate/20230118170602_add_extra_borrowing_value_check_to_sales.rb
  36. 5
      db/migrate/20230123160741_add_grant_value_check_to_sales_log.rb
  37. 21
      db/schema.rb
  38. 2
      db/seeds.rb
  39. 39
      spec/components/log_summary_component_spec.rb
  40. 5
      spec/factories/sales_log.rb
  41. 2
      spec/features/form/check_answers_page_lettings_logs_spec.rb
  42. 4
      spec/features/form/check_answers_page_sales_logs_spec.rb
  43. 2
      spec/features/organisation_spec.rb
  44. 22
      spec/helpers/tab_nav_helper_spec.rb
  45. 9
      spec/models/form/sales/questions/buyer1_income_known_spec.rb
  46. 2
      spec/models/form/sales/questions/buyer1_income_spec.rb
  47. 9
      spec/models/form/sales/questions/buyer2_income_known_spec.rb
  48. 2
      spec/models/form/sales/questions/buyer2_income_spec.rb
  49. 4
      spec/models/form/sales/questions/mortgage_length_spec.rb
  50. 2
      spec/models/form/sales/questions/number_of_others_in_property_spec.rb
  51. 5
      spec/models/form/sales/subsections/discounted_ownership_scheme_spec.rb
  52. 4
      spec/models/form_handler_spec.rb
  53. 6
      spec/models/lettings_log_spec.rb
  54. 2
      spec/models/organisation_spec.rb
  55. 19
      spec/models/sales_log_spec.rb
  56. 325
      spec/models/validations/sales/sale_information_validations_spec.rb
  57. 103
      spec/models/validations/sales/soft_validations_spec.rb
  58. 1
      spec/requests/lettings_logs_controller_spec.rb
  59. 9
      spec/requests/organisations_controller_spec.rb
  60. 19
      spec/requests/sales_logs_controller_spec.rb

12
app/components/log_summary_component.html.erb

@ -25,7 +25,7 @@
<% end %>
</header>
<% if log.is_a?(LettingsLog) && (log.needstype? or log.startdate?) %>
<% if log.lettings? && (log.needstype? or log.startdate?) %>
<p class="govuk-body govuk-!-margin-bottom-2">
<% if log.needstype? %>
<%= log.is_general_needs? ? "General needs" : "Supported housing" %><br>
@ -37,18 +37,20 @@
<% end %>
<% if current_user.support? || current_user.organisation.has_managing_agents? %>
<% if log.owning_organisation or log.managing_organisation %>
<dl class="app-metadata">
<dl class="app-metadata">
<% if log.owning_organisation %>
<div class="app-metadata__item">
<dt class="app-metadata__term">Owned by</dt>
<dd class="app-metadata__definition"><%= log.owning_organisation&.name %></dd>
</div>
<% end %>
<% if log.lettings? && log.managing_organisation %>
<div class="app-metadata__item">
<dt class="app-metadata__term">Managed by</dt>
<dd class="app-metadata__definition"><%= log.managing_organisation&.name %></dd>
</div>
</dl>
<% end %>
<% end %>
</dl>
<% end %>
</div>

6
app/controllers/lettings_logs_controller.rb

@ -101,6 +101,12 @@ class LettingsLogsController < LogsController
end
end
def org_params
super.merge(
{ "managing_organisation_id" => current_user.organisation.id },
)
end
private
def permitted_log_params

1
app/controllers/logs_controller.rb

@ -63,7 +63,6 @@ private
owning_organisation_id = current_user.organisation.holds_own_stock? ? current_user.organisation.id : nil
{
"owning_organisation_id" => owning_organisation_id,
"managing_organisation_id" => current_user.organisation.id,
"created_by_id" => current_user.id,
}
end

7
app/helpers/tab_nav_helper.rb

@ -21,11 +21,4 @@ module TabNavHelper
role = "<span class=\"app-!-colour-muted\">#{user.role.to_s.humanize}</span>"
[user.organisation.name, role].join("\n")
end
def tab_items(user)
[
{ name: t("Details"), url: details_organisation_path(user.organisation) },
{ name: t("Users"), url: users_organisation_path(user.organisation) },
]
end
end

2
app/models/form.rb

@ -61,7 +61,7 @@ class Form
page_ids = subsection_for_page(page).pages.map(&:id)
page_index = page_ids.index(page.id)
page_id = if page.id.include?("value_check") && log[page.questions[0].id] == 1 && page.routed_to?(log, current_user)
page_id = if page.interruption_screen? && log[page.questions[0].id] == 1 && page.routed_to?(log, current_user)
previous_page(page_ids, page_index, log, current_user)
else
page_ids[page_index + 1]

21
app/models/form/sales/pages/extra_borrowing_value_check.rb

@ -0,0 +1,21 @@
class Form::Sales::Pages::ExtraBorrowingValueCheck < Form::Page
def initialize(id, hsh, subsection)
super
@depends_on = [
{
"extra_borrowing_expected_but_not_reported?" => true,
},
]
@title_text = {
"translation" => "soft_validations.extra_borrowing.title",
}
@informative_text = {
}
end
def questions
@questions ||= [
Form::Sales::Questions::ExtraBorrowingValueCheck.new(nil, nil, self),
]
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

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

@ -4,6 +4,7 @@ class Form::Sales::Questions::Buyer1Income < ::Form::Question
@id = "income1"
@check_answer_label = "Buyer 1’s gross annual income"
@header = "Buyer 1’s gross annual income"
@hint_text = "Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments."
@type = "numeric"
@min = 0
@max = 999_999

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

@ -6,8 +6,6 @@ class Form::Sales::Questions::Buyer1IncomeKnown < ::Form::Question
@header = "Do you know buyer 1’s annual income?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@guidance_position = GuidancePosition::BOTTOM
@guidance_partial = "what_counts_as_income_sales"
@conditional_for = {
"income1" => [0],
}

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

@ -5,6 +5,7 @@ class Form::Sales::Questions::Buyer2Income < ::Form::Question
@check_answer_label = "Buyer 2’s gross annual income"
@header = "Buyer 2’s gross annual income"
@type = "numeric"
@hint_text = "Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments."
@min = 0
@step = 1
@width = 5

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

@ -6,8 +6,6 @@ class Form::Sales::Questions::Buyer2IncomeKnown < ::Form::Question
@header = "Do you know buyer 2’s annual income?"
@type = "radio"
@answer_options = ANSWER_OPTIONS
@guidance_position = GuidancePosition::BOTTOM
@guidance_partial = "what_counts_as_income_sales"
@conditional_for = {
"income2" => [0],
}

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

@ -0,0 +1,23 @@
class Form::Sales::Questions::ExtraBorrowingValueCheck < ::Form::Question
def initialize(id, hsh, page)
super(id, hsh, page)
@id = "extrabor_value_check"
@check_answer_label = "Extra borrowing confirmation"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"extrabor_value_check" => 0,
},
{
"extrabor_value_check" => 1,
},
],
}
@header = "Are you sure there is no extra borrowing?"
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?"
@type = "numeric"
@min = 0
@max = 999_999
@width = 5
@prefix = "£"
@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

@ -8,5 +8,6 @@ class Form::Sales::Questions::MortgageLength < ::Form::Question
@min = 0
@width = 5
@suffix = " years"
@hint_text = "You should round up to the nearest year. Value should not exceed 60 years."
end
end

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

@ -3,7 +3,7 @@ class Form::Sales::Questions::NumberOfOthersInProperty < ::Form::Question
super
@id = "hholdcount"
@check_answer_label = "Number of other people living in the property"
@header = "Besides the buyers, how many other people live in the property?"
@header = "Besides the buyer(s), how many other people live or will live in the property?"
@type = "numeric"
@hint_text = "You can provide details for a maximum of 4 other people."
@width = 2

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

@ -10,15 +10,20 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection
@pages ||= [
Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_discounted_ownership", 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::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::Mortgageused.new("mortgage_used_discounted_ownership", nil, self),
Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_mortgage_value_check", nil, self),
Form::Sales::Pages::MortgageLender.new("mortgage_lender_discounted_ownership", nil, self),
Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_discounted_ownership", nil, self),
Form::Sales::Pages::MortgageLength.new("mortgage_length_discounted_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_discounted_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_value_check", nil, self),
Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self),
Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_deposit_value_check", nil, self),
Form::Sales::Pages::DepositValueCheck.new("discounted_ownership_deposit_value_check", nil, self),
Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self),
]

14
app/models/lettings_log.rb

@ -35,6 +35,7 @@ class LettingsLog < Log
belongs_to :scheme, optional: true
belongs_to :location, optional: true
belongs_to :managing_organisation, class_name: "Organisation", optional: true
scope :filter_by_year, ->(year) { where(startdate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) }
scope :filter_by_tenant_code, ->(tenant_code) { where("tenancycode ILIKE ?", "%#{tenant_code}%") }
@ -50,6 +51,7 @@ class LettingsLog < Log
}
scope :filter_by_before_startdate, ->(date) { where("lettings_logs.startdate >= ?", date) }
scope :unresolved, -> { where(unresolved: true) }
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode].freeze
@ -495,6 +497,18 @@ class LettingsLog < Log
update(unresolved: false)
end
def managing_organisation_provider_type
managing_organisation&.provider_type
end
def reset_created_by!
return unless updated_by&.support?
return if owning_organisation.blank? || managing_organisation.blank? || created_by.blank?
return if created_by&.organisation == managing_organisation || created_by&.organisation == owning_organisation
update!(created_by: nil)
end
private
def reset_derived_questions

14
app/models/log.rb

@ -2,7 +2,6 @@ class Log < ApplicationRecord
self.abstract_class = true
belongs_to :owning_organisation, class_name: "Organisation", optional: true
belongs_to :managing_organisation, class_name: "Organisation", optional: true
belongs_to :created_by, class_name: "User", optional: true
belongs_to :updated_by, class_name: "User", optional: true
belongs_to :bulk_upload, optional: true
@ -12,7 +11,6 @@ class Log < ApplicationRecord
STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze
enum status: STATUS
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
scope :filter_by_status, ->(status, _user = nil) { where status: }
scope :filter_by_years, lambda { |years, _user = nil|
first_year = years.shift
@ -45,10 +43,6 @@ class Log < ApplicationRecord
ethnic_group == 17
end
def managing_organisation_provider_type
managing_organisation&.provider_type
end
def collection_period_open?
form.end_date > Time.zone.today
end
@ -115,14 +109,6 @@ private
reset_created_by!
end
def reset_created_by!
return unless updated_by&.support?
return if owning_organisation.blank? || managing_organisation.blank? || created_by.blank?
return if created_by&.organisation == managing_organisation || created_by&.organisation == owning_organisation
update!(created_by: nil)
end
PIO = PostcodeService.new
def process_previous_postcode_changes!

1
app/models/organisation.rb

@ -3,7 +3,6 @@ class Organisation < ApplicationRecord
has_many :owned_lettings_logs, class_name: "LettingsLog", foreign_key: "owning_organisation_id", dependent: :delete_all
has_many :managed_lettings_logs, class_name: "LettingsLog", foreign_key: "managing_organisation_id"
has_many :owned_sales_logs, class_name: "SalesLog", foreign_key: "owning_organisation_id", dependent: :delete_all
has_many :managed_sales_logs, class_name: "SalesLog", foreign_key: "managing_organisation_id"
has_many :data_protection_confirmations
has_many :organisation_rent_periods
has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id", dependent: :delete_all

9
app/models/sales_log.rb

@ -32,6 +32,7 @@ class SalesLog < Log
scope :filter_by_year, ->(year) { where(saledate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) }
scope :search_by, ->(param) { filter_by_id(param) }
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org) }
OPTIONAL_FIELDS = %w[purchid].freeze
RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze
@ -184,6 +185,14 @@ class SalesLog < Log
process_postcode(postcode_full, "pcodenk", "is_la_inferred", "la")
end
def reset_created_by!
return unless updated_by&.support?
return if owning_organisation.blank? || created_by.blank?
return if created_by&.organisation == owning_organisation
update!(created_by: nil)
end
def retirement_age_for_person(person_num)
gender = public_send("sex#{person_num}".to_sym)
return unless gender

1
app/models/user.rb

@ -10,7 +10,6 @@ class User < ApplicationRecord
has_many :owned_lettings_logs, through: :organisation
has_many :managed_lettings_logs, through: :organisation
has_many :owned_sales_logs, through: :organisation
has_many :managed_sales_logs, through: :organisation
has_many :legacy_users
has_many :bulk_uploads

4
app/models/validations/sales/household_validations.rb

@ -109,10 +109,6 @@ private
economic_status == 7
end
def person_economic_status_refused?(economic_status)
economic_status == 10
end
def person_is_child?(relationship)
relationship == "C"
end

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

@ -16,6 +16,29 @@ module Validations::Sales::SaleInformationValidations
end
end
def validate_years_living_in_property_before_purchase(record)
return unless record.proplen && record.proplen.nonzero?
case record.type
when 18
record.errors.add :type, I18n.t("validations.sale_information.proplen.social_homebuy")
record.errors.add :proplen, I18n.t("validations.sale_information.proplen.social_homebuy")
when 28, 29
record.errors.add :type, I18n.t("validations.sale_information.proplen.rent_to_buy")
record.errors.add :proplen, I18n.t("validations.sale_information.proplen.rent_to_buy")
end
end
def validate_exchange_date(record)
return unless record.exdate && record.saledate
record.errors.add(:exdate, I18n.t("validations.sale_information.exdate.must_be_before_saledate")) if record.exdate > record.saledate
return if (record.saledate.to_date - record.exdate.to_date).to_i / 365 < 1
record.errors.add(:exdate, I18n.t("validations.sale_information.exdate.must_be_less_than_1_year_from_saledate"))
end
def validate_previous_property_unit_type(record)
return unless record.fromprop && record.frombeds
@ -24,4 +47,20 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :fromprop, I18n.t("validations.sale_information.previous_property_type.property_type_bedsit")
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
end

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

@ -37,6 +37,13 @@ module Validations::Sales::SoftValidations
deposit > savings * 4 / 3
end
def extra_borrowing_expected_but_not_reported?
return unless extrabor && mortgage && deposit && value && discount
extrabor != 1 && mortgage + deposit > value - value * discount / 100
end
def hodate_3_years_or_more_exdate?
return unless hodate && exdate
@ -57,6 +64,12 @@ module Validations::Sales::SoftValidations
value < sale_range.soft_min ? sale_range.soft_min : sale_range.soft_max
end
def grant_outside_common_range?
return unless grant
!grant.between?(9_000, 16_000)
end
private
def sale_range

10
app/services/filter_service.rb

@ -17,6 +17,14 @@ class FilterService
logs = logs.public_send("filter_by_#{category}", values, user)
end
logs = logs.order(created_at: :desc)
user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs
if user.support?
if logs.first&.lettings?
logs.all.includes(:owning_organisation, :managing_organisation)
else
logs.all.includes(:owning_organisation)
end
else
logs
end
end
end

17
app/views/form/guidance/_what_counts_as_income_sales.html.erb

@ -1,17 +0,0 @@
<%= govuk_details(summary_text: "What counts as income?") do %>
<p class="govuk-body">You should include any income from:</p>
<ul class="govuk-list govuk-list--bullet">
<li>employment</li>
<li>pensions</li>
<li>investments</li>
<li>Universal Credit</li>
</ul>
<p class="govuk-body">Don’t include:</p>
<ul class="govuk-list govuk-list--bullet">
<li>National Insurance (NI) contributions and tax</li>
<li>housing benefit</li>
<li>child benefit</li>
<li>council tax support</li>
</ul>
<% end %>

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

@ -44,7 +44,7 @@
<%= f.hidden_field :page, value: @page.id %>
<div class="govuk-button-group">
<% if !@page.id.include?("value_check") && if request.query_parameters["referrer"] != "check_answers" %>
<% if !@page.interruption_screen? && if request.query_parameters["referrer"] != "check_answers" %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", send(@log.form.next_page_redirect_path(@page, @log, current_user), @log) %>
<% else %>

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

@ -1,6 +1,8 @@
<h2 class="govuk-body">
<%= 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>
<% logs.map do |log| %>
<%= render(LogSummaryComponent.new(current_user:, log:)) %>

2
config/environments/development.rb

@ -73,7 +73,7 @@ Rails.application.configure do
config.active_record.verbose_query_logs = true
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

2
config/environments/test.rb

@ -57,7 +57,7 @@ Rails.application.configure do
config.active_support.disallowed_deprecation_warnings = []
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

11
config/locales/en.yml

@ -413,6 +413,14 @@ en:
deactivation:
during_deactivated_period: "The location is already deactivated during this date, please enter a different date"
sale_information:
proplen:
social_homebuy: "Social HomeBuy buyers should not have lived here before"
rent_to_buy: "Rent to Buy buyers should not have lived here before"
exdate:
must_be_before_saledate:
Contract exchange date must be less than 1 year before completion date
must_be_less_than_1_year_from_saledate:
Contract exchange date must be less than 1 year before completion date
previous_property_beds:
property_type_bedsit: "Bedsit bedroom maximum 1"
previous_property_type:
@ -420,6 +428,7 @@ en:
handover_exchange:
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}"
soft_validations:
net_income:
@ -446,6 +455,8 @@ en:
max:
title: "You told us this person is %{age} or over and not retired"
hint_text: "The minimum expected retirement age for %{gender} in England is %{age}."
extra_borrowing:
title: "The mortgage and deposit are higher than the purchase minus the discount"
pregnancy:
title: "You told us somebody in the household is pregnant"
no_females: "You also told us there are no female tenants living at the property."

15450
config/rent_range_data/2022.csv

File diff suppressed because it is too large Load Diff

13
db/migrate/20230110094518_remove_managing_organisation_id_from_sales_logs.rb

@ -0,0 +1,13 @@
class RemoveManagingOrganisationIdFromSalesLogs < ActiveRecord::Migration[7.0]
def up
change_table :sales_logs, bulk: true do |t|
t.remove :managing_organisation_id
end
end
def down
change_table :sales_logs, bulk: true do |t|
t.column :managing_organisation_id, :bigint
end
end
end

7
db/migrate/20230118170602_add_extra_borrowing_value_check_to_sales.rb

@ -0,0 +1,7 @@
class AddExtraBorrowingValueCheckToSales < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :extrabor_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

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_23_160741) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -69,17 +69,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"
@ -378,7 +367,6 @@ ActiveRecord::Schema[7.0].define(version: 2023_01_24_111328) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "owning_organisation_id"
t.bigint "managing_organisation_id"
t.bigint "created_by_id"
t.string "purchid"
t.integer "type"
@ -498,9 +486,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"
@ -513,11 +501,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 "value_value_check"
t.integer "extrabor_value_check"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_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"
end

2
db/seeds.rb

@ -160,7 +160,7 @@ unless Rails.env.test?
saledate: Date.new(1, 1, 1),
purchid: "1",
ownershipsch: 2,
type: 8,
type: 9,
jointpur: 1,
jointmore: 1,
)

39
spec/components/log_summary_component_spec.rb

@ -5,15 +5,16 @@ RSpec.describe LogSummaryComponent, type: :component do
let(:coordinator_user) { FactoryBot.create(:user) }
let(:propcode) { "P3647" }
let(:tenancycode) { "T62863" }
let(:log) { FactoryBot.create(:lettings_log, needstype: 1, startdate: Time.utc(2022, 1, 1), tenancycode:, propcode:) }
let(:lettings_log) { FactoryBot.create(:lettings_log, needstype: 1, startdate: Time.utc(2022, 1, 1), tenancycode:, propcode:) }
let(:sales_log) { FactoryBot.create(:sales_log) }
context "when rendering log for a support user" do
context "when rendering lettings log for a support user" do
it "show the log summary with organisational relationships" do
result = render_inline(described_class.new(current_user: support_user, log:))
result = render_inline(described_class.new(current_user: support_user, log: lettings_log))
expect(result).to have_link(log.id.to_s)
expect(result).to have_text(log.tenancycode)
expect(result).to have_text(log.propcode)
expect(result).to have_link(lettings_log.id.to_s)
expect(result).to have_text(lettings_log.tenancycode)
expect(result).to have_text(lettings_log.propcode)
expect(result).to have_text("General needs")
expect(result).to have_text("Tenancy starts 1 January 2022")
expect(result).to have_text("Created 8 February 2022")
@ -23,12 +24,30 @@ RSpec.describe LogSummaryComponent, type: :component do
end
end
context "when rendering log for a data coordinator user" do
context "when rendering lettings log for a data coordinator user" do
it "show the log summary" do
result = render_inline(described_class.new(current_user: coordinator_user, log:))
result = render_inline(described_class.new(current_user: coordinator_user, log: lettings_log))
expect(result).not_to have_content("Owned by\n DLUHC")
expect(result).not_to have_content("Managed by\n DLUHC")
expect(result).not_to have_content("Owned by")
expect(result).not_to have_content("Managed by")
end
end
context "when rendering sales log for a support user" do
it "show the log summary with organisational relationships" do
result = render_inline(described_class.new(current_user: support_user, log: sales_log))
expect(result).to have_content("Owned by\n DLUHC")
expect(result).not_to have_content("Managed by")
end
end
context "when rendering sales log for a data coordinator user" do
it "show the log summary" do
result = render_inline(described_class.new(current_user: coordinator_user, log: sales_log))
expect(result).not_to have_content("Owned by")
expect(result).not_to have_content("Managed by")
end
end
end

5
spec/factories/sales_log.rb

@ -2,7 +2,6 @@ FactoryBot.define do
factory :sales_log do
created_by { FactoryBot.create(:user) }
owning_organisation { created_by.organisation }
managing_organisation { created_by.organisation }
created_at { Time.utc(2022, 2, 8, 16, 52, 15) }
updated_at { Time.utc(2022, 2, 8, 16, 52, 15) }
trait :in_progress do
@ -71,10 +70,10 @@ FactoryBot.define do
ecstat5 { 2 }
ecstat6 { 1 }
disabled { 1 }
deposit { 10_000 }
deposit { 80_000 }
cashdis { 1_000 }
value { 110_000 }
grant { 1_000 }
grant { 10_000 }
proplen { 10 }
pregyrha { 1 }
pregla { 1 }

2
spec/features/form/check_answers_page_lettings_logs_spec.rb

@ -143,7 +143,7 @@ RSpec.describe "Lettings Log Check Answers Page" do
end
context "when the user is checking their answers for the household characteristics subsection" do
it "they see a seperate summary card for each member of the household" do
it "they see a separate summary card for each member of the household" do
visit("/lettings-logs/#{completed_lettings_log.id}/#{subsection}/check-answers")
assert_selector ".x-govuk-summary-card__title", text: "Lead tenant", count: 1
assert_selector ".x-govuk-summary-card__title", text: "Person 2", count: 1

4
spec/features/form/check_answers_page_sales_logs_spec.rb

@ -39,7 +39,7 @@ RSpec.describe "Sales Log Check Answers Page" do
context "when the user is checking their answers for the household characteristics subsection" do
context "and the log is for a joint purchase" do
it "they see a seperate summary card for each member of the household" do
it "they see a separate summary card for each member of the household" do
visit("/sales-logs/#{completed_sales_log_joint_purchase.id}/#{subsection}/check-answers")
assert_selector ".x-govuk-summary-card__title", text: "Buyer 1", count: 1
assert_selector ".x-govuk-summary-card__title", text: "Buyer 2", count: 1
@ -49,7 +49,7 @@ RSpec.describe "Sales Log Check Answers Page" do
end
context "and the log is for a non-joint purchase" do
it "they see a seperate summary card for each member of the household" do
it "they see a separate summary card for each member of the household" do
visit("/sales-logs/#{completed_sales_log_non_joint_purchase.id}/#{subsection}/check-answers")
assert_selector ".x-govuk-summary-card__title", text: "Buyer 1", count: 1
assert_selector ".x-govuk-summary-card__title", text: "Buyer 2", count: 0

2
spec/features/organisation_spec.rb

@ -215,7 +215,7 @@ RSpec.describe "User Features" do
let(:number_of_sales_logs) { SalesLog.count }
before do
FactoryBot.create_list(:sales_log, 4, owning_organisation_id: organisation.id, managing_organisation_id: organisation.id)
FactoryBot.create_list(:sales_log, 4, owning_organisation_id: organisation.id)
visit("/organisations/#{org_id}/sales-logs")
end

22
spec/helpers/tab_nav_helper_spec.rb

@ -34,26 +34,4 @@ RSpec.describe TabNavHelper do
expect(scheme_cell(scheme)).to match(expected_html)
end
end
describe "#tab_items" do
context "when user is a data_coordinator" do
let(:user) { FactoryBot.build(:user, :data_coordinator, organisation:) }
it "returns details and user tabs" do
result = tab_items(user).map { |i| i[:name] }
expect(result.count).to eq(2)
expect(result.first).to match("Details")
expect(result.second).to match("Users")
end
end
context "when user is a data_provider" do
it "returns details and user tabs" do
result = tab_items(user).map { |i| i[:name] }
expect(result.count).to eq(2)
expect(result.first).to match("Details")
expect(result.second).to match("Users")
end
end
end
end

9
spec/models/form/sales/questions/buyer1_income_known_spec.rb

@ -44,15 +44,6 @@ RSpec.describe Form::Sales::Questions::Buyer1IncomeKnown, type: :model do
})
end
it "has the correct guidance_partial" do
expect(question.guidance_partial).to eq("what_counts_as_income_sales")
end
it "has the correct guidance position", :aggregate_failures do
expect(question.bottom_guidance?).to eq(true)
expect(question.top_guidance?).to eq(false)
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1)
end

2
spec/models/form/sales/questions/buyer1_income_spec.rb

@ -32,7 +32,7 @@ RSpec.describe Form::Sales::Questions::Buyer1Income, type: :model do
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
expect(question.hint_text).to eq("Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments.")
end
it "has correct width" do

9
spec/models/form/sales/questions/buyer2_income_known_spec.rb

@ -44,15 +44,6 @@ RSpec.describe Form::Sales::Questions::Buyer2IncomeKnown, type: :model do
})
end
it "has the correct guidance_partial" do
expect(question.guidance_partial).to eq("what_counts_as_income_sales")
end
it "has the correct guidance position", :aggregate_failures do
expect(question.bottom_guidance?).to eq(true)
expect(question.top_guidance?).to eq(false)
end
it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(2)
end

2
spec/models/form/sales/questions/buyer2_income_spec.rb

@ -32,7 +32,7 @@ RSpec.describe Form::Sales::Questions::Buyer2Income, type: :model do
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
expect(question.hint_text).to eq("Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments.")
end
it "has correct width" do

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

@ -32,7 +32,9 @@ RSpec.describe Form::Sales::Questions::MortgageLength, type: :model do
end
it "has the correct hint" do
expect(question.hint_text).to be_nil
expect(question.hint_text).to eq(
"You should round up to the nearest year. Value should not exceed 60 years.",
)
end
it "has correct width" do

2
spec/models/form/sales/questions/number_of_others_in_property_spec.rb

@ -16,7 +16,7 @@ RSpec.describe Form::Sales::Questions::NumberOfOthersInProperty, type: :model do
end
it "has the correct header" do
expect(question.header).to eq("Besides the buyers, how many other people live in the property?")
expect(question.header).to eq("Besides the buyer(s), how many other people live or will live in the property?")
end
it "has the correct check_answer_label" do

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

@ -16,15 +16,20 @@ RSpec.describe Form::Sales::Subsections::DiscountedOwnershipScheme, type: :model
%w[
living_before_purchase_discounted_ownership
about_price_rtb
extra_borrowing_price_value_check
about_price_not_rtb
grant_value_check
purchase_price_discounted_ownership
mortgage_used_discounted_ownership
mortgage_amount_discounted_ownership
extra_borrowing_mortgage_value_check
mortgage_lender_discounted_ownership
mortgage_lender_other_discounted_ownership
mortgage_length_discounted_ownership
extra_borrowing_discounted_ownership
extra_borrowing_value_check
about_deposit_discounted_ownership
extra_borrowing_deposit_value_check
discounted_ownership_deposit_value_check
leasehold_charges_discounted_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(184)
expect(form.pages.count).to eq(189)
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(184)
expect(form.pages.count).to eq(189)
expect(form.name).to eq("2021_2022_sales")
end
end

6
spec/models/lettings_log_spec.rb

@ -2392,12 +2392,6 @@ RSpec.describe LettingsLog do
FactoryBot.create(:lettings_log, :completed, owning_organisation: organisation_2, managing_organisation: organisation_2, created_by: nil)
end
it "filters by given organisation id" do
expect(described_class.filter_by_organisation([organisation_1.id]).count).to eq(3)
expect(described_class.filter_by_organisation([organisation_1.id, organisation_2.id]).count).to eq(4)
expect(described_class.filter_by_organisation([organisation_3.id]).count).to eq(0)
end
it "filters by given organisation" do
expect(described_class.filter_by_organisation([organisation_1]).count).to eq(3)
expect(described_class.filter_by_organisation([organisation_1, organisation_2]).count).to eq(4)

2
spec/models/organisation_spec.rb

@ -196,7 +196,7 @@ RSpec.describe Organisation, type: :model do
let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) }
let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) }
let!(:log_to_delete) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) }
let!(:sales_log_to_delete) { FactoryBot.create(:sales_log, owning_organisation: user.organisation, managing_organisation: user.organisation) }
let!(:sales_log_to_delete) { FactoryBot.create(:sales_log, owning_organisation: user.organisation) }
context "when organisation is deleted" do
it "child relationships ie logs, schemes and users are deleted too - application" do

19
spec/models/sales_log_spec.rb

@ -93,21 +93,14 @@ RSpec.describe SalesLog, type: :model do
let(:organisation_3) { create(:organisation) }
before do
create(:sales_log, :in_progress, owning_organisation: organisation_1, managing_organisation: organisation_1)
create(:sales_log, :completed, owning_organisation: organisation_1, managing_organisation: organisation_2)
create(:sales_log, :completed, owning_organisation: organisation_2, managing_organisation: organisation_1)
create(:sales_log, :completed, owning_organisation: organisation_2, managing_organisation: organisation_2)
end
it "filters by given organisation id" do
expect(described_class.filter_by_organisation([organisation_1.id]).count).to eq(3)
expect(described_class.filter_by_organisation([organisation_1.id, organisation_2.id]).count).to eq(4)
expect(described_class.filter_by_organisation([organisation_3.id]).count).to eq(0)
create(:sales_log, :in_progress, owning_organisation: organisation_1)
create(:sales_log, :completed, owning_organisation: organisation_1)
create(:sales_log, :completed, owning_organisation: organisation_2)
end
it "filters by given organisation" do
expect(described_class.filter_by_organisation([organisation_1]).count).to eq(3)
expect(described_class.filter_by_organisation([organisation_1, organisation_2]).count).to eq(4)
expect(described_class.filter_by_organisation([organisation_1]).count).to eq(2)
expect(described_class.filter_by_organisation([organisation_1, organisation_2]).count).to eq(3)
expect(described_class.filter_by_organisation([organisation_3]).count).to eq(0)
end
end
@ -165,7 +158,6 @@ RSpec.describe SalesLog, type: :model do
FactoryBot.create(
:sales_log,
:completed,
managing_organisation: owning_organisation,
owning_organisation:,
created_by: created_by_user,
pcodenk: 0,
@ -297,7 +289,6 @@ RSpec.describe SalesLog, type: :model do
let!(:address_sales_log) do
described_class.create({
managing_organisation: owning_organisation,
owning_organisation:,
created_by: created_by_user,
ppcodenk: 1,

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

@ -5,48 +5,6 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
let(:validator_class) { Class.new { include Validations::Sales::SaleInformationValidations } }
describe "#validate_deposit_range" do
context "when within permitted bounds" do
let(:record) { build(:sales_log, deposit: 0) }
it "does not add an error" do
sale_information_validator.validate_deposit_range(record)
expect(record.errors[:deposit]).not_to be_present
end
end
context "when blank" do
let(:record) { build(:sales_log, deposit: nil) }
it "does not add an error" do
sale_information_validator.validate_deposit_range(record)
expect(record.errors[:deposit]).not_to be_present
end
end
context "when below lower bound" do
let(:record) { build(:sales_log, deposit: -1) }
it "adds an error" do
sale_information_validator.validate_deposit_range(record)
expect(record.errors[:deposit]).to be_present
end
end
context "when higher than upper bound" do
let(:record) { build(:sales_log, deposit: 1_000_000) }
it "adds an error" do
sale_information_validator.validate_deposit_range(record)
expect(record.errors[:deposit]).to be_present
end
end
end
describe "#validate_practical_completion_date_before_exdate" do
context "when hodate blank" do
let(:record) { build(:sales_log, hodate: nil) }
@ -109,6 +67,82 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
end
describe "#validate_exchange_date" do
context "when exdate blank" do
let(:record) { build(:sales_log, exdate: nil) }
it "does not add an error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).not_to be_present
end
end
context "when saledate blank" do
let(:record) { build(:sales_log, saledate: nil) }
it "does not add an error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).not_to be_present
end
end
context "when saledate and exdate blank" do
let(:record) { build(:sales_log, exdate: nil, saledate: nil) }
it "does not add an error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).not_to be_present
end
end
context "when exdate before saledate" do
let(:record) { build(:sales_log, exdate: 2.months.ago, saledate: 1.month.ago) }
it "does not add the error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).not_to be_present
end
end
context "when exdate more than 1 year before saledate" do
let(:record) { build(:sales_log, exdate: 2.years.ago, saledate: 1.month.ago) }
it "does not add the error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).to eq(
["Contract exchange date must be less than 1 year before completion date"],
)
end
end
context "when exdate after saledate" do
let(:record) { build(:sales_log, exdate: 1.month.ago, saledate: 2.months.ago) }
it "adds error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).to eq(
["Contract exchange date must be less than 1 year before completion date"],
)
end
end
context "when exdate == saledate" do
let(:record) { build(:sales_log, exdate: Time.zone.parse("2023-07-01"), saledate: Time.zone.parse("2023-07-01")) }
it "does not add an error" do
sale_information_validator.validate_exchange_date(record)
expect(record.errors[:exdate]).not_to be_present
end
end
end
describe "#validate_previous_property_unit_type" do
context "when number of bedrooms is <= 1" do
let(:record) { FactoryBot.build(:sales_log, frombeds: 1, fromprop: 2) }
@ -130,4 +164,211 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
end
end
describe "#validate_years_living_in_property_before_purchase" do
context "when proplen blank" do
let(:record) { build(:sales_log, proplen: nil) }
it "does not add an error" do
sale_information_validator.validate_years_living_in_property_before_purchase(record)
expect(record.errors).not_to be_present
end
end
context "when type blank" do
let(:record) { build(:sales_log, type: nil) }
it "does not add an error" do
sale_information_validator.validate_years_living_in_property_before_purchase(record)
expect(record.errors).not_to be_present
end
end
context "when proplen 0" do
let(:record) { build(:sales_log, proplen: 0) }
it "does not add an error" do
sale_information_validator.validate_years_living_in_property_before_purchase(record)
expect(record.errors).not_to be_present
end
end
context "when type Rent to Buy and proplen > 0" do
let(:record) { build(:sales_log, proplen: 1, type: 28) }
it "adds an error" do
sale_information_validator.validate_years_living_in_property_before_purchase(record)
expect(record.errors[:type]).to include(I18n.t("validations.sale_information.proplen.rent_to_buy"))
expect(record.errors[:proplen]).to include(I18n.t("validations.sale_information.proplen.rent_to_buy"))
end
end
context "when type Social HomeBuy and proplen > 0" do
let(:record) { build(:sales_log, proplen: 1, type: 18) }
it "adds an error" do
sale_information_validator.validate_years_living_in_property_before_purchase(record)
expect(record.errors[:type]).to include(I18n.t("validations.sale_information.proplen.social_homebuy"))
expect(record.errors[:proplen]).to include(I18n.t("validations.sale_information.proplen.social_homebuy"))
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
end

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

@ -128,7 +128,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_mortgage_over_soft_max
end
it "returns true if only income1 is used for morgage and it is less than 1/5 of the morgage" do
it "returns true if only income1 is used for mortgage and it is less than 1/5 of the mortgage" do
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 51_000
@ -137,7 +137,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.to be_mortgage_over_soft_max
end
it "returns false if only income1 is used for morgage and it is more than 1/5 of the morgage" do
it "returns false if only income1 is used for mortgage and it is more than 1/5 of the mortgage" do
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 44_000
@ -146,7 +146,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_mortgage_over_soft_max
end
it "returns true if only income2 is used for morgage and it is less than 1/5 of the morgage" do
it "returns true if only income2 is used for mortgage and it is less than 1/5 of the mortgage" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
@ -155,7 +155,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.to be_mortgage_over_soft_max
end
it "returns false if only income2 is used for morgage and it is more than 1/5 of the morgage" do
it "returns false if only income2 is used for mortgage and it is more than 1/5 of the mortgage" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
@ -164,7 +164,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_mortgage_over_soft_max
end
it "returns true if income1 and income2 are used for morgage and their sum is less than 1/5 of the morgage" do
it "returns true if income1 and income2 are used for mortgage and their sum is less than 1/5 of the mortgage" do
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 10_000
@ -174,7 +174,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.to be_mortgage_over_soft_max
end
it "returns false if income1 and income2 are used for morgage and their sum is more than 1/5 of the morgage" do
it "returns false if income1 and income2 are used for mortgage and their sum is more than 1/5 of the mortgage" do
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 8_000
@ -184,7 +184,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_mortgage_over_soft_max
end
it "returns true if neither of the incomes are used for morgage and the morgage is more than 0" do
it "returns true if neither of the incomes are used for mortgage and the mortgage is more than 0" do
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 124_000
@ -192,7 +192,7 @@ RSpec.describe Validations::Sales::SoftValidations do
.to be_mortgage_over_soft_max
end
it "returns true if neither of the incomes are used for morgage and the morgage is 0" do
it "returns false if neither of the incomes are used for mortgage and the mortgage is 0" do
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 0
@ -200,6 +200,73 @@ RSpec.describe Validations::Sales::SoftValidations do
.not_to be_mortgage_over_soft_max
end
end
context "when validating extra borrowing" do
it "returns false if extrabor not present" do
record.mortgage = 50_000
record.deposit = 40_000
record.value = 100_000
record.discount = 11
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns false if mortgage not present" do
record.extrabor = 2
record.deposit = 40_000
record.value = 100_000
record.discount = 11
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns false if deposit not present" do
record.extrabor = 2
record.mortgage = 50_000
record.value = 100_000
record.discount = 11
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns false if value not present" do
record.extrabor = 2
record.mortgage = 50_000
record.deposit = 40_000
record.discount = 11
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns false if discount not present" do
record.extrabor = 2
record.mortgage = 50_000
record.deposit = 40_000
record.value = 100_000
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns false if extra borrowing expected and reported" do
record.extrabor = 1
record.mortgage = 50_000
record.deposit = 40_000
record.value = 100_000
record.discount = 11
expect(record)
.not_to be_extra_borrowing_expected_but_not_reported
end
it "returns true if extra borrowing expected but not reported" do
record.extrabor = 2
record.mortgage = 50_000
record.deposit = 40_000
record.value = 100_000
record.discount = 11
expect(record)
.to be_extra_borrowing_expected_but_not_reported
end
end
end
describe "savings amount validations" do
@ -392,4 +459,24 @@ RSpec.describe Validations::Sales::SoftValidations do
expect(record).not_to be_purchase_price_out_of_soft_range
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

1
spec/requests/lettings_logs_controller_spec.rb

@ -458,6 +458,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "includes the search on the CSV link" do
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: {}
expect(page).to have_link("Download (CSV)", href: "/lettings-logs/csv-download?search=#{search_term}")
end

9
spec/requests/organisations_controller_spec.rb

@ -734,8 +734,8 @@ RSpec.describe OrganisationsController, type: :request do
let(:number_of_org2_sales_logs) { 4 }
before do
FactoryBot.create_list(:sales_log, number_of_org1_sales_logs, owning_organisation_id: organisation.id, managing_organisation_id: organisation.id)
FactoryBot.create_list(:sales_log, number_of_org2_sales_logs, owning_organisation_id: unauthorised_organisation.id, managing_organisation_id: unauthorised_organisation.id)
FactoryBot.create_list(:sales_log, number_of_org1_sales_logs, owning_organisation_id: organisation.id)
FactoryBot.create_list(:sales_log, number_of_org2_sales_logs, owning_organisation_id: unauthorised_organisation.id)
get "/organisations/#{organisation.id}/sales-logs", headers:, params: {}
end
@ -1141,10 +1141,11 @@ RSpec.describe OrganisationsController, type: :request do
context "when they view the logs tab" do
before do
FactoryBot.create(:lettings_log, owning_organisation: organisation)
get "/organisations/#{organisation.id}/lettings-logs"
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")
end
@ -1152,7 +1153,7 @@ RSpec.describe OrganisationsController, type: :request do
let(:other_organisation) { FactoryBot.create(:organisation) }
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)
end

19
spec/requests/sales_logs_controller_spec.rb

@ -3,7 +3,6 @@ require "rails_helper"
RSpec.describe SalesLogsController, type: :request do
let(:user) { FactoryBot.create(:user) }
let(:owning_organisation) { user.organisation }
let(:managing_organisation) { owning_organisation }
let(:api_username) { "test_user" }
let(:api_password) { "test_password" }
let(:basic_credentials) do
@ -14,7 +13,6 @@ RSpec.describe SalesLogsController, type: :request do
let(:params) do
{
"owning_organisation_id": owning_organisation.id,
"managing_organisation_id": managing_organisation.id,
"created_by_id": user.id,
}
end
@ -51,7 +49,6 @@ RSpec.describe SalesLogsController, type: :request do
it "creates a sales log with the values passed" do
json_response = JSON.parse(response.body)
expect(json_response["owning_organisation_id"]).to eq(owning_organisation.id)
expect(json_response["managing_organisation_id"]).to eq(managing_organisation.id)
expect(json_response["created_by_id"]).to eq(user.id)
end
@ -94,14 +91,12 @@ RSpec.describe SalesLogsController, type: :request do
FactoryBot.create(
:sales_log,
owning_organisation: organisation,
managing_organisation: organisation,
)
end
let!(:unauthorized_sales_log) do
FactoryBot.create(
:sales_log,
owning_organisation: other_organisation,
managing_organisation: other_organisation,
)
end
@ -119,7 +114,7 @@ RSpec.describe SalesLogsController, type: :request do
it "does have organisation values" do
get "/sales-logs", headers: headers, params: {}
expect(page).to have_content("Owned by")
expect(page).to have_content("Managed by")
expect(page).not_to have_content("Managed by")
end
it "shows sales logs for all organisations" do
@ -146,13 +141,11 @@ RSpec.describe SalesLogsController, type: :request do
let!(:not_started_sales_log) do
FactoryBot.create(:sales_log,
owning_organisation: organisation,
managing_organisation: organisation,
created_by: user)
end
let!(:completed_sales_log) do
FactoryBot.create(:sales_log, :completed,
owning_organisation: organisation_2,
managing_organisation: organisation,
created_by: user_2)
end
@ -189,14 +182,12 @@ RSpec.describe SalesLogsController, type: :request do
let!(:sales_log_2021) do
FactoryBot.create(:sales_log, :in_progress,
owning_organisation: organisation,
saledate: Time.zone.local(2022, 3, 1),
managing_organisation: organisation)
saledate: Time.zone.local(2022, 3, 1))
end
let!(:sales_log_2022) do
sales_log = FactoryBot.build(:sales_log, :completed,
owning_organisation: organisation,
saledate: Time.zone.local(2022, 12, 1),
managing_organisation: organisation)
saledate: Time.zone.local(2022, 12, 1))
sales_log.save!(validate: false)
sales_log
end
@ -227,14 +218,12 @@ RSpec.describe SalesLogsController, type: :request do
FactoryBot.create(:sales_log, :completed,
owning_organisation: organisation,
saledate: Time.zone.local(2022, 3, 1),
managing_organisation: organisation,
created_by: user)
end
let!(:sales_log_2022) do
FactoryBot.create(:sales_log,
owning_organisation: organisation,
saledate: Time.zone.local(2022, 12, 1),
managing_organisation: organisation,
created_by: user)
end
@ -362,7 +351,7 @@ RSpec.describe SalesLogsController, type: :request do
context "when there are more than 20 logs" do
before do
FactoryBot.create_list(:sales_log, 25, owning_organisation: organisation, managing_organisation: organisation)
FactoryBot.create_list(:sales_log, 25, owning_organisation: organisation)
end
context "when on the first page" do

Loading…
Cancel
Save