Browse Source

Merge branch 'main' into CLDC-1528-person-1-relationship-to-buyer-1

# Conflicts:
#	db/schema.rb
CLDC-1528-person-1-relationship-to-buyer-1
natdeanlewissoftwire 2 years ago
parent
commit
ed2afc16fb
  1. 6
      Gemfile.lock
  2. 115
      app/controllers/organisation_relationships_controller.rb
  3. 2
      app/helpers/tasklist_helper.rb
  4. 21
      app/models/form/sales/pages/buyer1_income_value_check.rb
  5. 21
      app/models/form/sales/pages/mortgage_value_check.rb
  6. 1
      app/models/form/sales/questions/buyer1_income.rb
  7. 25
      app/models/form/sales/questions/buyer1_income_value_check.rb
  8. 25
      app/models/form/sales/questions/mortgage_value_check.rb
  9. 2
      app/models/form/sales/subsections/income_benefits_and_savings.rb
  10. 13
      app/models/organisation_relationship.rb
  11. 51
      app/models/sales_log.rb
  12. 14
      app/models/validations/sales/financial_validations.rb
  13. 23
      app/models/validations/sales/soft_validations.rb
  14. 2
      app/views/devise/unlocks/new.html.erb
  15. 2
      app/views/organisation_relationships/_related_organisation_select_question.html.erb
  16. 3
      app/views/organisation_relationships/add_housing_provider.html.erb
  17. 3
      app/views/organisation_relationships/add_managing_agent.html.erb
  18. 2
      app/views/organisation_relationships/managing_agents.html.erb
  19. 9
      config/locales/en.yml
  20. 2
      db/migrate/20221212161657_add_details_known1_to_sales_log.rb
  21. 10
      db/migrate/20221213085819_add_mortgage_and_value_checks.rb
  22. 9
      db/schema.rb
  23. 2
      spec/factories/sales_log.rb
  24. 33
      spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
  25. 33
      spec/models/form/sales/pages/mortgage_value_check_spec.rb
  26. 4
      spec/models/form/sales/questions/buyer1_income_spec.rb
  27. 61
      spec/models/form/sales/questions/buyer1_income_value_check_spec.rb
  28. 61
      spec/models/form/sales/questions/mortgage_value_check_spec.rb
  29. 2
      spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb
  30. 56
      spec/models/validations/sales/financial_validations_spec.rb
  31. 204
      spec/models/validations/sales/soft_validations_spec.rb
  32. 16
      spec/requests/organisation_relationships_controller_spec.rb

6
Gemfile.lock

@ -199,7 +199,7 @@ GEM
listen (3.7.1) listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.19.0) loofah (2.19.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.1) mail (2.7.1)
@ -294,8 +294,8 @@ GEM
rails-dom-testing (2.0.3) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.4.3) rails-html-sanitizer (1.4.4)
loofah (~> 2.3) loofah (~> 2.19, >= 2.19.1)
railties (7.0.4) railties (7.0.4)
actionpack (= 7.0.4) actionpack (= 7.0.4)
activesupport (= 7.0.4) activesupport (= 7.0.4)

115
app/controllers/organisation_relationships_controller.rb

@ -5,13 +5,19 @@ class OrganisationRelationshipsController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :authenticate_scope! before_action :authenticate_scope!
before_action :organisations
before_action :target_organisation, only: %i[
remove_housing_provider
remove_managing_agent
delete_housing_provider
delete_managing_agent
]
def housing_providers def housing_providers
housing_providers = organisation.housing_providers housing_providers = organisation.housing_providers
unpaginated_filtered_housing_providers = filtered_collection(housing_providers, search_term) unpaginated_filtered_housing_providers = filtered_collection(housing_providers, search_term)
organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
@pagy, @housing_providers = pagy(unpaginated_filtered_housing_providers) @pagy, @housing_providers = pagy(unpaginated_filtered_housing_providers)
@organisations = organisations
@searched = search_term.presence @searched = search_term.presence
@total_count = housing_providers.size @total_count = housing_providers.size
end end
@ -19,107 +25,84 @@ class OrganisationRelationshipsController < ApplicationController
def managing_agents def managing_agents
managing_agents = organisation.managing_agents managing_agents = organisation.managing_agents
unpaginated_filtered_managing_agents = filtered_collection(managing_agents, search_term) unpaginated_filtered_managing_agents = filtered_collection(managing_agents, search_term)
organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name)
respond_to :html
@pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents) @pagy, @managing_agents = pagy(unpaginated_filtered_managing_agents)
@organisations = organisations
@searched = search_term.presence @searched = search_term.presence
@total_count = managing_agents.size @total_count = managing_agents.size
end end
def add_housing_provider def add_housing_provider
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name) @organisation_relationship = organisation.parent_organisation_relationships.new
respond_to :html
end end
def add_managing_agent def add_managing_agent
@organisations = Organisation.where.not(id: @organisation.id).pluck(:id, :name) @organisation_relationship = organisation.child_organisation_relationships.new
respond_to :html
end end
def create_housing_provider def create_housing_provider
child_organisation = @organisation @organisation_relationship = organisation.parent_organisation_relationships.new(organisation_relationship_params)
if params[:organisation][:related_organisation_id].empty? if @organisation_relationship.save(context: :housing_provider)
@organisation.errors.add :related_organisation_id, "You must choose a housing provider" flash[:notice] = "#{@organisation_relationship.parent_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers"
@organisations = Organisation.where.not(id: child_organisation.id).pluck(:id, :name) redirect_to housing_providers_organisation_path
render "organisation_relationships/add_housing_provider"
return
else else
parent_organisation = related_organisation @organisations = Organisation.where.not(id: organisation.id).pluck(:id, :name)
if OrganisationRelationship.exists?(child_organisation:, parent_organisation:) render "organisation_relationships/add_housing_provider", status: :unprocessable_entity
@organisation.errors.add :related_organisation_id, "You have already added this housing provider"
@organisations = Organisation.where.not(id: child_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_housing_provider"
return
end
end end
create!(child_organisation:, parent_organisation:)
flash[:notice] = "#{related_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers"
redirect_to housing_providers_organisation_path
end end
def create_managing_agent def create_managing_agent
parent_organisation = @organisation @organisation_relationship = organisation.child_organisation_relationships.new(organisation_relationship_params)
if params[:organisation][:related_organisation_id].empty? if @organisation_relationship.save
@organisation.errors.add :related_organisation_id, "You must choose a managing agent" flash[:notice] = "#{@organisation_relationship.child_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
@organisations = Organisation.where.not(id: parent_organisation.id).pluck(:id, :name) redirect_to managing_agents_organisation_path
render "organisation_relationships/add_managing_agent"
return
else else
child_organisation = related_organisation @organisations = Organisation.where.not(id: organisation.id).pluck(:id, :name)
if OrganisationRelationship.exists?(child_organisation:, parent_organisation:) render "organisation_relationships/add_managing_agent", status: :unprocessable_entity
@organisation.errors.add :related_organisation_id, "You have already added this managing agent"
@organisations = Organisation.where.not(id: parent_organisation.id).pluck(:id, :name)
render "organisation_relationships/add_managing_agent"
return
end end
end end
create!(child_organisation:, parent_organisation:)
flash[:notice] = "#{related_organisation.name} is now one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
redirect_to managing_agents_organisation_path
end
def remove_housing_provider def remove_housing_provider; end
@target_organisation_id = target_organisation.id
end
def delete_housing_provider def delete_housing_provider
relationship = OrganisationRelationship.find_by!( OrganisationRelationship.find_by!(
child_organisation: @organisation, child_organisation: organisation,
parent_organisation: target_organisation, parent_organisation: target_organisation,
) ).destroy!
relationship.destroy!
flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers" flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} housing providers"
redirect_to housing_providers_organisation_path redirect_to housing_providers_organisation_path
end end
def remove_managing_agent def remove_managing_agent; end
@target_organisation_id = target_organisation.id
end
def delete_managing_agent def delete_managing_agent
relationship = OrganisationRelationship.find_by!( OrganisationRelationship.find_by!(
parent_organisation: @organisation, parent_organisation: organisation,
child_organisation: target_organisation, child_organisation: target_organisation,
) ).destroy!
relationship.destroy!
flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents" flash[:notice] = "#{target_organisation.name} is no longer one of #{current_user.data_coordinator? ? 'your' : "this organisation's"} managing agents"
redirect_to managing_agents_organisation_path redirect_to managing_agents_organisation_path
end end
private private
def create!(child_organisation:, parent_organisation:) def organisation
@resource = OrganisationRelationship.new(child_organisation:, parent_organisation:) @organisation ||= if current_user.support?
@resource.save! Organisation.find(params[:id])
else
current_user.organisation
end
end end
def organisation def organisations
@organisation ||= Organisation.find(params[:id]) @organisations ||= Organisation.where.not(id: organisation.id).pluck(:id, :name)
end end
def related_organisation def parent_organisation
@related_organisation ||= Organisation.find(params[:organisation][:related_organisation_id]) @parent_organisation ||= Organisation.find(params[:organisation_relationship][:parent_organisation_id])
end
def child_organisation
@child_organisation ||= Organisation.find(params[:organisation_relationship][:child_organisation_id])
end end
def target_organisation def target_organisation
@ -130,8 +113,12 @@ private
params["search"] params["search"]
end end
def organisation_relationship_params
params.require(:organisation_relationship).permit(:parent_organisation_id, :child_organisation_id)
end
def authenticate_scope! def authenticate_scope!
if current_user.organisation != organisation && !current_user.support? if current_user.organisation != Organisation.find(params[:id]) && !current_user.support?
render_not_found render_not_found
end end
end end

2
app/helpers/tasklist_helper.rb

@ -39,7 +39,7 @@ module TasklistHelper
def review_log_text(log) def review_log_text(log)
if log.collection_period_open? if log.collection_period_open?
"You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(log)} until #{(log.form.end_date).to_formatted_s(:govuk_date)}.".html_safe "You can #{govuk_link_to 'review and make changes to this log', review_lettings_log_path(log)} until #{log.form.end_date.to_formatted_s(:govuk_date)}.".html_safe
else else
"This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed." "This log is from the #{log.form.start_date.year}/#{log.form.start_date.year + 1} collection window, which is now closed."
end end

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

@ -0,0 +1,21 @@
class Form::Sales::Pages::Buyer1IncomeValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "buyer_1_income_value_check"
@header = ""
@description = ""
@subsection = subsection
@depends_on = [
{
"income1_under_soft_min?" => true,
},
]
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::Buyer1IncomeValueCheck.new(nil, nil, self),
]
end
end

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

@ -0,0 +1,21 @@
class Form::Sales::Pages::MortgageValueCheck < ::Form::Page
def initialize(id, hsh, subsection)
super
@id = "mortgage_value_check"
@header = ""
@description = ""
@subsection = subsection
@depends_on = [
{
"mortgage_over_soft_max?" => true,
},
]
@informative_text = {}
end
def questions
@questions ||= [
Form::Sales::Questions::MortgageValueCheck.new(nil, nil, self),
]
end
end

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

@ -7,6 +7,7 @@ class Form::Sales::Questions::Buyer1Income < ::Form::Question
@type = "numeric" @type = "numeric"
@page = page @page = page
@min = 0 @min = 0
@max = 999_999
@step = 1 @step = 1
@width = 5 @width = 5
@prefix = "£" @prefix = "£"

25
app/models/form/sales/questions/buyer1_income_value_check.rb

@ -0,0 +1,25 @@
class Form::Sales::Questions::Buyer1IncomeValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "income1_value_check"
@check_answer_label = "Income confirmation"
@header = "Are you sure this income is correct?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"income1_value_check" => 0,
},
{
"income1_value_check" => 1,
},
],
}
@check_answers_card_number = 1
@page = page
end
end

25
app/models/form/sales/questions/mortgage_value_check.rb

@ -0,0 +1,25 @@
class Form::Sales::Questions::MortgageValueCheck < ::Form::Question
def initialize(id, hsh, page)
super
@id = "mortgage_value_check"
@check_answer_label = "Mortgage confirmation"
@header = "Are you sure that the mortgage is more than 5 times the income used for the mortgage application?"
@type = "interruption_screen"
@answer_options = {
"0" => { "value" => "Yes" },
"1" => { "value" => "No" },
}
@hidden_in_check_answers = {
"depends_on" => [
{
"mortgage_value_check" => 0,
},
{
"mortgage_value_check" => 1,
},
],
}
@check_answers_card_number = 1
@page = page
end
end

2
app/models/form/sales/subsections/income_benefits_and_savings.rb

@ -10,8 +10,10 @@ class Form::Sales::Subsections::IncomeBenefitsAndSavings < ::Form::Subsection
def pages def pages
@pages ||= [ @pages ||= [
Form::Sales::Pages::Buyer1Income.new(nil, nil, self), Form::Sales::Pages::Buyer1Income.new(nil, nil, self),
Form::Sales::Pages::Buyer1IncomeValueCheck.new(nil, nil, self),
Form::Sales::Pages::Buyer1Mortgage.new(nil, nil, self), Form::Sales::Pages::Buyer1Mortgage.new(nil, nil, self),
Form::Sales::Pages::Buyer2Income.new(nil, nil, self), Form::Sales::Pages::Buyer2Income.new(nil, nil, self),
Form::Sales::Pages::MortgageValueCheck.new(nil, nil, self),
Form::Sales::Pages::Savings.new(nil, nil, self), Form::Sales::Pages::Savings.new(nil, nil, self),
Form::Sales::Pages::PreviousOwnership.new(nil, nil, self), Form::Sales::Pages::PreviousOwnership.new(nil, nil, self),
] ]

13
app/models/organisation_relationship.rb

@ -1,4 +1,17 @@
class OrganisationRelationship < ApplicationRecord class OrganisationRelationship < ApplicationRecord
belongs_to :child_organisation, class_name: "Organisation" belongs_to :child_organisation, class_name: "Organisation"
belongs_to :parent_organisation, class_name: "Organisation" belongs_to :parent_organisation, class_name: "Organisation"
validates :parent_organisation_id, presence: { message: I18n.t("validations.organisation.housing_provider.blank") }
validates :child_organisation_id, presence: { message: I18n.t("validations.organisation.managing_agent.blank") }
validates :parent_organisation_id, uniqueness: { scope: :child_organisation_id, message: I18n.t("validations.organisation.housing_provider.already_added") }
validates :child_organisation_id, uniqueness: { scope: :parent_organisation_id, message: I18n.t("validations.organisation.managing_agent.already_added") }
validate :validate_housing_provider_owns_stock, on: :housing_provider
private
def validate_housing_provider_owns_stock
if parent_organisation_id.present? && !parent_organisation.holds_own_stock
errors.add :parent_organisation_id, I18n.t("validations.organisation.housing_provider.does_not_own_stock")
end
end
end end

51
app/models/sales_log.rb

@ -1,5 +1,7 @@
class SalesLogValidator < ActiveModel::Validator class SalesLogValidator < ActiveModel::Validator
include Validations::Sales::HouseholdValidations include Validations::Sales::HouseholdValidations
include Validations::SharedValidations
include Validations::Sales::FinancialValidations
def validate(record) def validate(record)
validation_methods = public_methods.select { |method| method.starts_with?("validate_") } validation_methods = public_methods.select { |method| method.starts_with?("validate_") }
@ -9,6 +11,7 @@ end
class SalesLog < Log class SalesLog < Log
include DerivedVariables::SalesLogVariables include DerivedVariables::SalesLogVariables
include Validations::Sales::SoftValidations
self.inheritance_column = :_type_disabled self.inheritance_column = :_type_disabled
@ -60,4 +63,52 @@ class SalesLog < Log
def unresolved def unresolved
false false
end end
LONDON_BOROUGHS = %w[
E09000001
E09000033
E09000020
E09000013
E09000032
E09000022
E09000028
E09000030
E09000012
E09000019
E09000007
E09000005
E09000009
E09000018
E09000027
E09000021
E09000024
E09000029
E09000008
E09000006
E09000023
E09000011
E09000004
E09000016
E09000002
E09000026
E09000025
E09000031
E09000014
E09000010
E09000003
E09000015
E09000017
].freeze
def london_property?
la && LONDON_BOROUGHS.include?(la)
end
def income1_used_for_mortgage?
inc1mort == 1
end
def income2_used_for_mortgage?
inc2mort == 1
end
end end

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

@ -0,0 +1,14 @@
module Validations::Sales::FinancialValidations
# Validations methods need to be called 'validate_<page_name>' to run on model save
# or 'validate_' to run on submit as well
def validate_income1(record)
if record.ecstat1 && record.income1 && record.ownershipsch == 1
if record.london_property?
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000
elsif record.income1 > 80_000
record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000)
end
end
end
end

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

@ -0,0 +1,23 @@
module Validations::Sales::SoftValidations
ALLOWED_INCOME_RANGES = {
1 => OpenStruct.new(soft_min: 5000),
2 => OpenStruct.new(soft_min: 1500),
3 => OpenStruct.new(soft_min: 1000),
5 => OpenStruct.new(soft_min: 2000),
0 => OpenStruct.new(soft_min: 2000),
}.freeze
def income1_under_soft_min?
return false unless ecstat1 && income1 && ALLOWED_INCOME_RANGES[ecstat1]
income1 < ALLOWED_INCOME_RANGES[ecstat1][:soft_min]
end
def mortgage_over_soft_max?
return false unless mortgage && inc1mort && inc2mort
return false if income1_used_for_mortgage? && income1.blank? || income2_used_for_mortgage? && income2.blank?
income_used_for_mortgage = (income1_used_for_mortgage? ? income1 : 0) + (income2_used_for_mortgage? ? income2 : 0)
mortgage > income_used_for_mortgage * 5
end
end

2
app/views/devise/unlocks/new.html.erb

@ -1,7 +1,7 @@
<h2>Resend unlock instructions</h2> <h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %> <%= render "devise/shared/error_messages", resource: %>
<%= f.govuk_email_field :email, <%= f.govuk_email_field :email,
label: { text: "Email address" }, label: { text: "Email address" },

2
app/views/organisation_relationships/_related_organisation_select_question.html.erb

@ -1,3 +1,3 @@
<% answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } %> <% answers = question.answer_options.map { |key, value| OpenStruct.new(id: key, name: value) } %>
<%= f.govuk_collection_select :related_organisation_id, answers, :id, :name, label: { hidden: true }, "data-controller": "accessible-autocomplete" do %> <%= f.govuk_collection_select field, answers, :id, :name, label: { hidden: true }, "data-controller": "accessible-autocomplete" do %>
<% end %> <% end %>

3
app/views/organisation_relationships/add_housing_provider.html.erb

@ -1,4 +1,4 @@
<%= form_with model: @organisation, url: housing_providers_organisation_path, method: "post", local: true do |f| %> <%= form_with model: @organisation_relationship, url: housing_providers_organisation_path, method: "post", local: true do |f| %>
<% if current_user.support? %> <% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %> <%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
@ -18,6 +18,7 @@
<% answer_options[organisation[0]] = organisation[1] %> <% answer_options[organisation[0]] = organisation[1] %>
<% end %> <% end %>
<%= render partial: "organisation_relationships/related_organisation_select_question", locals: { <%= render partial: "organisation_relationships/related_organisation_select_question", locals: {
field: :parent_organisation_id,
question: Form::Question.new("", { "answer_options" => answer_options }, nil), question: Form::Question.new("", { "answer_options" => answer_options }, nil),
f:, f:,
} %> } %>

3
app/views/organisation_relationships/add_managing_agent.html.erb

@ -1,4 +1,4 @@
<%= form_with model: @organisation, url: managing_agents_organisation_path, method: "post", local: true do |f| %> <%= form_with model: @organisation_relationship, url: managing_agents_organisation_path, method: "post", local: true do |f| %>
<% if current_user.support? %> <% if current_user.support? %>
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %> <%= render SubNavigationComponent.new(items: secondary_items(request.path, @organisation.id)) %>
@ -18,6 +18,7 @@
<% answer_options[organisation[0]] = organisation[1] %> <% answer_options[organisation[0]] = organisation[1] %>
<% end %> <% end %>
<%= render partial: "organisation_relationships/related_organisation_select_question", locals: { <%= render partial: "organisation_relationships/related_organisation_select_question", locals: {
field: :child_organisation_id,
question: Form::Question.new("", { "answer_options" => answer_options }, nil), question: Form::Question.new("", { "answer_options" => answer_options }, nil),
f:, f:,
} %> } %>

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

@ -11,7 +11,7 @@
<p class="govuk-body">This organisation does not currently have any managing agents.</p> <p class="govuk-body">This organisation does not currently have any managing agents.</p>
<% end %> <% end %>
<% else %> <% else %>
<%= render partial: "organisations/headings", locals: { main: "This organisation managing agents", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: { main: "Your managing agents", sub: current_user.organisation.name } %>
<p class="govuk-body">A managing agent can submit logs for this organisation.</p> <p class="govuk-body">A managing agent can submit logs for this organisation.</p>
<% if @total_count == 0 %> <% if @total_count == 0 %>
<p class="govuk-body">This organisation does not currently have any managing agents.</p> <p class="govuk-body">This organisation does not currently have any managing agents.</p>

9
config/locales/en.yml

@ -116,6 +116,13 @@ en:
organisation: organisation:
name_missing: "Enter the name of the organisation" name_missing: "Enter the name of the organisation"
provider_type_missing: "Select the organisation type" provider_type_missing: "Select the organisation type"
housing_provider:
blank: "You must choose a stock owner"
already_added: "You have already added this stock owner"
does_not_own_stock: "You can only add stock owners who own stock, which this organisation does not."
managing_agent:
blank: "You must choose a managing agent"
already_added: "You have already added this managing agent"
not_answered: "You must answer %{question}" not_answered: "You must answer %{question}"
other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided" other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided"
@ -194,6 +201,8 @@ en:
under_hard_min: "Net income cannot be less than £%{hard_min} per week given the tenant’s working situation" under_hard_min: "Net income cannot be less than £%{hard_min} per week given the tenant’s working situation"
freq_missing: "Select how often the household receives income" freq_missing: "Select how often the household receives income"
earnings_missing: "Enter how much income the household has in total" earnings_missing: "Enter how much income the household has in total"
income1:
over_hard_max: "Income must be lower than £%{hard_max}"
negative_currency: "Enter an amount above 0" negative_currency: "Enter an amount above 0"
rent: rent:
less_than_shortfall: "Enter an amount that is more than the shortfall in basic rent" less_than_shortfall: "Enter an amount that is more than the shortfall in basic rent"

2
db/migrate/20221212161657_add_details_known1_to_sales_log.rb

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

10
db/migrate/20221213085819_add_mortgage_and_value_checks.rb

@ -0,0 +1,10 @@
class AddMortgageAndValueChecks < ActiveRecord::Migration[7.0]
def change
change_table :sales_logs, bulk: true do |t|
t.column :income1_value_check, :integer
t.column :mortgage, :integer
t.column :inc2mort, :integer
t.column :mortgage_value_check, :integer
end
end
end

9
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: 2022_12_13_130736) do ActiveRecord::Schema[7.0].define(version: 2022_12_13_085819) 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"
@ -399,12 +399,15 @@ ActiveRecord::Schema[7.0].define(version: 2022_12_13_130736) do
t.integer "inc1mort" t.integer "inc1mort"
t.integer "income2" t.integer "income2"
t.integer "income2nk" t.integer "income2nk"
t.integer "prevown"
t.integer "savingsnk" t.integer "savingsnk"
t.integer "savings" t.integer "savings"
t.integer "prevown"
t.string "sex3" t.string "sex3"
t.integer "details_known_1" t.integer "details_known_1"
t.string "relat3" t.integer "income1_value_check"
t.integer "mortgage"
t.integer "inc2mort"
t.integer "mortgage_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 ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_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 ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id"

2
spec/factories/sales_log.rb

@ -57,11 +57,13 @@ FactoryBot.define do
inc1mort { 1 } inc1mort { 1 }
income2nk { 0 } income2nk { 0 }
income2 { 10_000 } income2 { 10_000 }
inc2mort { 1 }
la_known { "1" } la_known { "1" }
la { "E09000003" } la { "E09000003" }
savingsnk { 1 } savingsnk { 1 }
prevown { 1 } prevown { 1 }
sex3 { "X" } sex3 { "X" }
mortgage { 20_000 }
end end
end end
end end

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

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::Buyer1IncomeValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
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[income1_value_check])
end
it "has the correct id" do
expect(page.id).to eq("buyer_1_income_value_check")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"income1_under_soft_min?" => true,
},
])
end
end

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

@ -0,0 +1,33 @@
require "rails_helper"
RSpec.describe Form::Sales::Pages::MortgageValueCheck, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
let(:page_id) { nil }
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[mortgage_value_check])
end
it "has the correct id" do
expect(page.id).to eq("mortgage_value_check")
end
it "has the correct header" do
expect(page.header).to eq("")
end
it "has correct depends_on" do
expect(page.depends_on).to eq([
{
"mortgage_over_soft_max?" => true,
},
])
end
end

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

@ -54,4 +54,8 @@ RSpec.describe Form::Sales::Questions::Buyer1Income, type: :model do
it "has the correct check_answers_card_number" do it "has the correct check_answers_card_number" do
expect(question.check_answers_card_number).to eq(1) expect(question.check_answers_card_number).to eq(1)
end end
it "has correct max" do
expect(question.max).to eq(999_999)
end
end end

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

@ -0,0 +1,61 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::Buyer1IncomeValueCheck, 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("income1_value_check")
end
it "has the correct header" do
expect(question.header).to eq("Are you sure this income is correct?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Income 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(1)
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" => [
{
"income1_value_check" => 0,
},
{
"income1_value_check" => 1,
},
],
})
end
end

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

@ -0,0 +1,61 @@
require "rails_helper"
RSpec.describe Form::Sales::Questions::MortgageValueCheck, 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("mortgage_value_check")
end
it "has the correct header" do
expect(question.header).to eq("Are you sure that the mortgage is more than 5 times the income used for the mortgage application?")
end
it "has the correct check_answer_label" do
expect(question.check_answer_label).to eq("Mortgage 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(1)
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" => [
{
"mortgage_value_check" => 0,
},
{
"mortgage_value_check" => 1,
},
],
})
end
end

2
spec/models/form/sales/subsections/income_benefits_and_savings_spec.rb

@ -15,8 +15,10 @@ RSpec.describe Form::Sales::Subsections::IncomeBenefitsAndSavings, type: :model
expect(subsection.pages.map(&:id)).to eq( expect(subsection.pages.map(&:id)).to eq(
%w[ %w[
buyer_1_income buyer_1_income
buyer_1_income_value_check
buyer_1_mortgage buyer_1_mortgage
buyer_2_income buyer_2_income
mortgage_value_check
savings savings
previous_ownership previous_ownership
], ],

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

@ -0,0 +1,56 @@
require "rails_helper"
RSpec.describe Validations::Sales::FinancialValidations do
subject(:financial_validator) { validator_class.new }
let(:validator_class) { Class.new { include Validations::Sales::FinancialValidations } }
describe "income validations" do
let(:record) { FactoryBot.create(:sales_log, ownershipsch: 1) }
context "with shared ownership" do
context "and non london borough" do
(0..8).each do |ecstat|
it "adds an error when buyer 1 income is over hard max for ecstat #{ecstat}" do
record.income1 = 85_000
record.ecstat1 = ecstat
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000))
end
end
it "validates that the income is within the expected range for the tenant’s employment status" do
record.income1 = 75_000
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
end
end
context "and a london borough" do
before do
record.update!(la: "E09000030")
record.reload
end
(0..8).each do |ecstat|
it "adds an error when buyer 1 income is over hard max for ecstat #{ecstat}" do
record.income1 = 95_000
record.ecstat1 = ecstat
financial_validator.validate_income1(record)
expect(record.errors["income1"])
.to include(match I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000))
end
end
it "validates that the income is within the expected range for the tenant’s employment status" do
record.income1 = 85_000
record.ecstat1 = 1
financial_validator.validate_income1(record)
expect(record.errors["income1"]).to be_empty
end
end
end
end
end

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

@ -0,0 +1,204 @@
require "rails_helper"
RSpec.describe Validations::Sales::SoftValidations do
let(:record) { FactoryBot.create(:sales_log) }
describe "income1 min validations" do
context "when validating soft min" do
it "returns false if no income1 is given" do
record.income1 = nil
expect(record)
.not_to be_income1_under_soft_min
end
it "returns false if no ecstat1 is given" do
record.ecstat1 = nil
expect(record)
.not_to be_income1_under_soft_min
end
[
{
income1: 4500,
ecstat1: 1,
},
{
income1: 1400,
ecstat1: 2,
},
{
income1: 999,
ecstat1: 3,
},
{
income1: 1899,
ecstat1: 5,
},
{
income1: 1888,
ecstat1: 0,
},
].each do |test_case|
it "returns true if income1 is below soft min for ecstat1 #{test_case[:ecstat1]}" do
record.income1 = test_case[:income1]
record.ecstat1 = test_case[:ecstat1]
expect(record)
.to be_income1_under_soft_min
end
end
[
{
income1: 5001,
ecstat1: 1,
},
{
income1: 1600,
ecstat1: 2,
},
{
income1: 1004,
ecstat1: 3,
},
{
income1: 2899,
ecstat1: 4,
},
{
income1: 2899,
ecstat1: 5,
},
{
income1: 5,
ecstat1: 6,
},
{
income1: 10_888,
ecstat1: 0,
},
].each do |test_case|
it "returns false if income1 is over soft min for ecstat1 #{test_case[:ecstat1]}" do
record.income1 = test_case[:income1]
record.ecstat1 = test_case[:ecstat1]
expect(record)
.not_to be_income1_under_soft_min
end
end
end
end
describe "mortgage amount validations" do
context "when validating soft max" do
it "returns false if no mortgage is given" do
record.mortgage = nil
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no inc1mort is given" do
record.inc1mort = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no inc2mort is given" do
record.inc1mort = 2
record.inc2mort = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no income1 is given and inc1mort is yes" do
record.inc1mort = 1
record.inc2mort = 2
record.income1 = nil
record.mortgage = 20_000
expect(record)
.not_to be_mortgage_over_soft_max
end
it "returns false if no income2 is given and inc2mort is yes" do
record.inc1mort = 2
record.inc2mort = 1
record.income2 = nil
record.mortgage = 20_000
expect(record)
.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
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 51_000
record.inc2mort = 2
expect(record)
.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
record.inc1mort = 1
record.income1 = 10_000
record.mortgage = 44_000
record.inc2mort = 2
expect(record)
.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
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
record.mortgage = 51_000
expect(record)
.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
record.inc1mort = 2
record.inc2mort = 1
record.income2 = 10_000
record.mortgage = 44_000
expect(record)
.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
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 10_000
record.income2 = 10_000
record.mortgage = 101_000
expect(record)
.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
record.inc1mort = 1
record.inc2mort = 1
record.income1 = 8_000
record.income2 = 17_000
record.mortgage = 124_000
expect(record)
.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
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 124_000
expect(record)
.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
record.inc1mort = 2
record.inc2mort = 2
record.mortgage = 0
expect(record)
.not_to be_mortgage_over_soft_max
end
end
end
end

16
spec/requests/organisation_relationships_controller_spec.rb

@ -139,8 +139,8 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
let(:params) do let(:params) do
{ {
"organisation": { "organisation_relationship": {
"related_organisation_id": housing_provider.id, "parent_organisation_id": housing_provider.id,
}, },
} }
end end
@ -167,8 +167,8 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
let(:params) do let(:params) do
{ {
"organisation": { "organisation_relationship": {
"related_organisation_id": managing_agent.id, "child_organisation_id": managing_agent.id,
}, },
} }
end end
@ -368,8 +368,8 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
let(:params) do let(:params) do
{ {
"organisation": { "organisation_relationship": {
"related_organisation_id": housing_provider.id, "parent_organisation_id": housing_provider.id,
}, },
} }
end end
@ -396,8 +396,8 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
let(:params) do let(:params) do
{ {
"organisation": { "organisation_relationship": {
"related_organisation_id": managing_agent.id, "child_organisation_id": managing_agent.id,
}, },
} }
end end

Loading…
Cancel
Save