Submit social housing lettings and sales data (CORE)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

463 lines
11 KiB

class SalesLogValidator < ActiveModel::Validator
include Validations::Sales::SetupValidations
include Validations::Sales::HouseholdValidations
include Validations::Sales::PropertyValidations
include Validations::Sales::FinancialValidations
include Validations::Sales::SaleInformationValidations
include Validations::SharedValidations
include Validations::LocalAuthorityValidations
def validate(record)
validation_methods = public_methods.select { |method| method.starts_with?("validate_") }
validation_methods.each { |meth| public_send(meth, record) }
end
end
class SalesLog < Log
include DerivedVariables::SalesLogVariables
include Validations::Sales::SoftValidations
include Validations::SoftValidations
include MoneyFormattingHelper
self.inheritance_column = :_type_disabled
has_paper_trail
validates_with SalesLogValidator
before_validation :recalculate_start_year!, if: :saledate_changed?
before_validation :process_postcode_changes!, if: :postcode_full_changed?
before_validation :process_previous_postcode_changes!, if: :ppostcode_full_changed?
before_validation :reset_invalidated_dependent_fields!
before_validation :reset_location_fields!, unless: :postcode_known?
before_validation :reset_previous_location_fields!, unless: :previous_postcode_known?
before_validation :set_derived_fields!
before_validation :process_uprn_change!, if: :should_process_uprn_change?
belongs_to :managing_organisation, class_name: "Organisation", optional: true
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 :filter_by_purchaser_code, ->(purchid) { where("purchid ILIKE ?", "%#{purchid}%") }
scope :search_by, lambda { |param|
filter_by_purchaser_code(param)
.or(filter_by_postcode(param))
.or(filter_by_id(param))
}
scope :age1_answered, -> { where.not(age1: nil).or(where(age1_known: [1, 2])) }
scope :duplicate_logs, lambda { |log|
visible.where(log.slice(*DUPLICATE_LOG_ATTRIBUTES))
.where.not(id: log.id)
.where.not(saledate: nil)
.where.not(sex1: nil)
.where.not(ecstat1: nil)
.where.not(postcode_full: nil)
.age1_answered
}
scope :after_date, ->(date) { where("saledate >= ?", date) }
scope :duplicate_sets, lambda { |created_by_id = nil|
scope = visible
.group(*DUPLICATE_LOG_ATTRIBUTES)
.where.not(saledate: nil)
.where.not(sex1: nil)
.where.not(ecstat1: nil)
.where.not(postcode_full: nil)
.age1_answered
.having("COUNT(*) > 1")
if created_by_id
scope = scope.having("MAX(CASE WHEN created_by_id = ? THEN 1 ELSE 0 END) >= 1", created_by_id)
end
scope.pluck("ARRAY_AGG(id)")
}
OPTIONAL_FIELDS = %w[purchid othtype].freeze
RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze
DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id purchid saledate age1_known age1 sex1 ecstat1 postcode_full].freeze
def lettings?
false
end
def sales?
true
end
def startdate
saledate
end
def self.editable_fields
attribute_names
end
def purchaser_code
purchid
end
def form_name
return unless saledate
FormHandler.instance.form_name_from_start_year(collection_start_year, "sales")
end
def form
FormHandler.instance.get_form(form_name) || FormHandler.instance.current_sales_form
end
def optional_fields
OPTIONAL_FIELDS + dynamically_not_required
end
def dynamically_not_required
not_required = []
not_required << "proplen" if proplen_optional?
not_required << "mortlen" if mortlen_optional?
not_required << "frombeds" if frombeds_optional?
not_required |= %w[address_line2 county postcode_full] if saledate && collection_start_year_for_date(saledate) >= 2023
not_required
end
def proplen_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def mortlen_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def frombeds_optional?
return false unless collection_start_year
collection_start_year < 2023
end
def unresolved
false
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 property_not_in_london?
!london_property?
end
def income1_used_for_mortgage?
inc1mort == 1
end
def buyers_will_live_in?
buylivein == 1
end
def buyers_will_not_live_in?
buylivein == 2
end
def buyer_two_will_live_in_property?
buy2livein == 1
end
def buyer_two_will_not_live_in_property?
buy2livein == 2
end
def buyer_one_will_not_live_in_property?
buy1livein == 2
end
def buyer_two_not_already_living_in_property?
buy2living == 2
end
def income2_used_for_mortgage?
inc2mort == 1
end
def right_to_buy?
[9, 14, 27].include?(type)
end
def rent_to_buy_full_ownership?
type == 29
end
def outright_sale_or_discounted_with_full_ownership?
ownershipsch == 3 || (ownershipsch == 2 && rent_to_buy_full_ownership?)
end
def is_type_discount?
type == 18
end
def ppostcode_full=(postcode)
if postcode
super UKPostcode.parse(postcode).to_s
else
super nil
end
end
def previous_postcode_known?
ppcodenk&.zero?
end
def postcode_known?
pcodenk&.zero?
end
def postcode_full=(postcode)
if postcode
super UKPostcode.parse(postcode).to_s
else
super nil
end
end
def expected_shared_ownership_deposit_value
return unless value && equity
value * equity / 100
end
def mortgage_deposit_and_discount_total
mortgage_amount = mortgage || 0
deposit_amount = deposit || 0
cashdis_amount = cashdis || 0
mortgage_amount + deposit_amount + cashdis_amount
end
def mortgage_deposit_and_discount_error_fields
[
"mortgage",
"deposit",
cashdis.present? ? "discount" : nil,
].compact.to_sentence
end
def mortgage_and_deposit_total
return unless mortgage && deposit
mortgage + deposit
end
def outright_sale?
ownershipsch == 3
end
def discounted_ownership_sale?
ownershipsch == 2
end
def mortgage_used?
mortgageused == 1
end
def mortgage_not_used?
mortgageused == 2
end
def process_postcode_changes!
self.postcode_full = upcase_and_remove_whitespace(postcode_full)
return if postcode_full.blank?
self.pcodenk = 0
inferred_la = get_inferred_la(postcode_full)
self.is_la_inferred = inferred_la.present?
self.la = inferred_la if inferred_la.present?
end
def process_previous_postcode_changes!
self.ppostcode_full = upcase_and_remove_whitespace(ppostcode_full)
return if ppostcode_full.blank?
self.ppcodenk = 0
inferred_la = get_inferred_la(ppostcode_full)
self.is_previous_la_inferred = inferred_la.present?
self.prevloc = inferred_la if inferred_la.present?
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 == owning_organisation || created_by&.organisation == managing_organisation
return if created_by&.organisation == owning_organisation.absorbing_organisation || created_by&.organisation == managing_organisation.absorbing_organisation
update!(created_by: nil)
end
def retirement_age_for_person(person_num)
gender = public_send("sex#{person_num}".to_sym)
return unless gender
RETIREMENT_AGES[gender]
end
def joint_purchase?
jointpur == 1
end
def not_joint_purchase?
jointpur == 2
end
def buyer_has_seen_privacy_notice?
privacynotice == 1
end
def buyer_not_interviewed?
noint == 1
end
def old_persons_shared_ownership?
type == 24
end
def is_bedsit?
proptype == 2
end
def shared_ownership_scheme?
ownershipsch == 1
end
def company_buyer?
companybuy == 1
end
def no_monthly_leasehold_charges?
has_mscharge&.zero?
end
def buyers_age_for_old_persons_shared_ownership_invalid?
return unless old_persons_shared_ownership?
(joint_purchase? && ages_unknown_or_under_64?([1, 2])) || (not_joint_purchase? && ages_unknown_or_under_64?([1]))
end
def ages_unknown_or_under_64?(person_indexes)
person_indexes.all? { |person_num| self["age#{person_num}"].present? && self["age#{person_num}"] < 64 || self["age#{person_num}_known"] == 1 }
end
def purchase_price_soft_min
LaSaleRange.find_by(start_year: collection_start_year, la:, bedrooms: beds).soft_min
end
def purchase_price_soft_max
LaSaleRange.find_by(start_year: collection_start_year, la:, bedrooms: beds).soft_max
end
def income_soft_min_for_ecstat(ecstat_field)
economic_status_code = public_send(ecstat_field)
return unless ALLOWED_INCOME_RANGES_SALES
soft_min = ALLOWED_INCOME_RANGES_SALES[economic_status_code]&.soft_min
format_as_currency(soft_min)
end
def should_process_uprn_change?
uprn && saledate && (uprn_changed? || saledate_changed?) && collection_start_year_for_date(saledate) >= 2023
end
def value_with_discount
return if value.blank?
discount_amount = discount ? value * discount / 100 : 0
value - discount_amount
end
def mortgage_deposit_and_grant_total
return if deposit.blank?
grant_amount = grant || 0
mortgage_amount = mortgage || 0
mortgage_amount + deposit + grant_amount
end
def beds_for_la_sale_range
beds.nil? ? nil : [beds, LaSaleRange::MAX_BEDS].min
end
def ownership_scheme(uppercase: false)
ownership_scheme = case ownershipsch
when 1 then "shared ownership"
when 2 then "discounted ownership"
when 3 then "outright or other sale"
end
uppercase ? ownership_scheme.capitalize : ownership_scheme
end
def combined_income
buyer_1_income = income1 || 0
buyer_2_income = income2 || 0
buyer_1_income + buyer_2_income
end
def blank_compound_invalid_non_setup_fields!
super
self.pcodenk = nil if errors.attribute_names.include? :postcode_full
end
def duplicate_check_question_ids
["owning_organisation_id",
"saledate",
"purchid",
"age1",
"sex1",
"ecstat1",
form.start_date.year < 2023 || uprn.blank? ? "postcode_full" : nil,
form.start_date.year >= 2023 && uprn.present? ? "uprn" : nil].compact
end
def duplicates
SalesLog.where.not(duplicate_set_id: nil).where(duplicate_set_id:).where.not(id:)
end
end