class Scheme < ApplicationRecord belongs_to :owning_organisation, class_name: "Organisation" has_many :locations, dependent: :delete_all has_many :lettings_logs, class_name: "LettingsLog", dependent: :delete_all has_many :scheme_deactivation_periods, class_name: "SchemeDeactivationPeriod" has_paper_trail scope :filter_by_id, ->(id) { where(id: (id.start_with?("S") ? id[1..] : id)) } scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") } scope :search_by_postcode, ->(postcode) { where("schemes.id IN (SELECT DISTINCT scheme_id FROM locations WHERE REPLACE(locations.postcode, ' ', '') ILIKE ?)", "%#{postcode.delete(' ')}%") } scope :search_by_location_name, ->(name) { where("schemes.id IN (SELECT DISTINCT scheme_id FROM locations WHERE locations.name ILIKE ?)", "%#{name}%") } scope :search_by, lambda { |param| search_by_postcode(param) .or(search_by_service_name(param)) .or(search_by_location_name(param)) .or(filter_by_id(param)) } scope :order_by_service_name, lambda { order("lower(service_name) ASC") } scope :filter_by_owning_organisation, ->(owning_organisation, _user = nil) { where(owning_organisation:) } scope :filter_by_status, lambda { |statuses, _user = nil| filtered_records = all scopes = [] statuses.each do |status| status = status == "active" ? "active_status" : status if respond_to?(status, true) scopes << send(status) end end if scopes.any? filtered_records = filtered_records .left_outer_joins(:scheme_deactivation_periods) .merge(scopes.reduce(&:or)) end filtered_records } scope :incomplete, lambda { where.not(confirmed: true) .or(where(confirmed: nil)) .or(where.not(id: Location.select(:scheme_id).where(confirmed: true).distinct)) .where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivated.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id)) } scope :deactivated, lambda { merge(SchemeDeactivationPeriod.deactivations_without_reactivation) .where("scheme_deactivation_periods.deactivation_date <= ?", Time.zone.now) } scope :deactivating_soon, lambda { merge(SchemeDeactivationPeriod.deactivations_without_reactivation) .where("scheme_deactivation_periods.deactivation_date > ? AND scheme_deactivation_periods.deactivation_date < ? ", Time.zone.now, 6.months.from_now) } scope :reactivating_soon, lambda { where.not("scheme_deactivation_periods.reactivation_date IS NULL") .where("scheme_deactivation_periods.reactivation_date > ?", Time.zone.now) } scope :active_status, lambda { where.not(id: joins(:scheme_deactivation_periods).reactivating_soon.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivated.pluck(:id)) .where.not(id: incomplete.pluck(:id)) .where.not(id: joins(:scheme_deactivation_periods).deactivating_soon.pluck(:id)) } validate :validate_confirmed validate :validate_owning_organisation auto_strip_attributes :service_name, squish: true SENSITIVE = { No: 0, Yes: 1, }.freeze enum sensitive: SENSITIVE, _suffix: true REGISTERED_UNDER_CARE_ACT = { "Yes – registered care home providing nursing care": 4, "Yes – registered care home providing personal care": 3, "Yes – part registered as a care home": 2, "No": 1, }.freeze enum registered_under_care_act: REGISTERED_UNDER_CARE_ACT SCHEME_TYPE = { "Direct Access Hostel": 5, "Foyer": 4, "Housing for older people": 7, "Other Supported Housing": 6, "Missing": 0, }.freeze enum scheme_type: SCHEME_TYPE, _suffix: true SUPPORT_TYPE = { "Missing": 0, "Low level": 2, "Medium level": 3, "High level": 4, "Nursing care in a care home": 5, "Floating support": 6, }.freeze enum support_type: SUPPORT_TYPE, _suffix: true PRIMARY_CLIENT_GROUP = { "Homeless families with support needs": "O", "Offenders and people at risk of offending": "H", "Older people with support needs": "M", "People at risk of domestic violence": "L", "People with a physical or sensory disability": "A", "People with alcohol problems": "G", "People with drug problems": "F", "People with HIV or AIDS": "B", "People with learning disabilities": "D", "People with mental health problems": "E", "Refugees (permanent)": "I", "Rough sleepers": "S", "Single homeless people with support needs": "N", "Teenage parents": "R", "Young people at risk": "Q", "Young people leaving care": "P", "Missing": "X", }.freeze enum primary_client_group: PRIMARY_CLIENT_GROUP, _suffix: true enum secondary_client_group: PRIMARY_CLIENT_GROUP, _suffix: true INTENDED_STAY = { "Very short stay": "V", "Short stay": "S", "Medium stay": "M", "Permanent": "P", "Missing": "X", }.freeze HAS_OTHER_CLIENT_GROUP = { No: 0, Yes: 1, }.freeze enum intended_stay: INTENDED_STAY, _suffix: true enum has_other_client_group: HAS_OTHER_CLIENT_GROUP, _suffix: true ARRANGEMENT_TYPE = { "The same organisation that owns the housing stock": "D", "Another registered stock owner": "R", "A registered charity or voluntary organisation": "V", "Another organisation": "O", "Missing": "X", }.freeze enum arrangement_type: ARRANGEMENT_TYPE, _suffix: true def self.find_by_id_on_multiple_fields(scheme_id, location_id) return if scheme_id.nil? if scheme_id.start_with?("S") where(id: scheme_id[1..]).first elsif location_id.present? joins(:locations).where("ltrim(schemes.old_visible_id, '0') = ? AND ltrim(locations.old_visible_id, '0') = ?", scheme_id.to_s, location_id.to_s).first || where("ltrim(schemes.old_visible_id, '0') = ?", scheme_id.to_s).first else where("ltrim(old_visible_id, '0') = ?", scheme_id.to_s).first end end def id_to_display "S#{id}" end def check_details_attributes [ { name: "Scheme code", value: id_to_display, id: "id" }, { name: "Name", value: service_name, id: "service_name" }, { name: "Confidential information", value: sensitive, id: "sensitive" }, { name: "Type of scheme", value: scheme_type, id: "scheme_type" }, { name: "Registered under Care Standards Act 2000", value: registered_under_care_act, id: "registered_under_care_act" }, { name: "Housing stock owned by", value: owning_organisation.name, id: "owning_organisation_id" }, { name: "Support services provided by", value: arrangement_type, id: "arrangement_type" }, ] end def check_primary_client_attributes [ { name: "Primary client group", value: primary_client_group, id: "primary_client_group" }, ] end def check_secondary_client_confirmation_attributes [ { name: "Has another client group", value: has_other_client_group, id: "has_other_client_group" }, ] end def check_secondary_client_attributes [ { name: "Secondary client group", value: secondary_client_group, id: "secondary_client_group" }, ] end def check_support_attributes [ { name: "Level of support given", value: support_type, id: "support_type" }, { name: "Intended length of stay", value: intended_stay, id: "intended_stay" }, ] end def care_acts_options_with_hints hints = { "Yes – part registered as a care home": "A proportion of units are registered as being a care home." } Scheme.registered_under_care_acts.keys.map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) } end def support_level_options_with_hints hints = { "Low level": "Staff visiting once a week, fortnightly or less.", "Medium level": "Staff on site daily or making frequent visits with some out-of-hours cover.", "High level": "Intensive level of staffing provided on a 24-hour basis.", } Scheme.support_types.keys.excluding("Missing").excluding("Floating support").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) } end def intended_length_of_stay_options_with_hints hints = { "Very short stay": "Up to one month.", "Short stay": "Up to one year.", "Medium stay": "More than one year but with an expectation to move on.", "Permanent": "Provides a home for life with no requirement for the tenant to move.", } Scheme.intended_stays.keys.excluding("Missing").map { |key, _| OpenStruct.new(id: key, name: key.to_s.humanize, description: hints[key.to_sym]) } end def validate_confirmed required_attributes = attribute_names - %w[id created_at updated_at old_id old_visible_id confirmed end_date sensitive secondary_client_group total_units deactivation_date deactivation_date_type] if confirmed == true required_attributes.any? do |attribute| if self[attribute].blank? errors.add attribute.to_sym self.confirmed = false end end end end def validate_owning_organisation unless owning_organisation&.holds_own_stock? errors.add(:owning_organisation_id, :does_not_own_stock, message: I18n.t("validations.scheme.owning_organisation.does_not_own_stock")) end end def available_from FormHandler.instance.earliest_open_collection_start_date(now: created_at) end def open_deactivation scheme_deactivation_periods.deactivations_without_reactivation.first end def last_deactivation_before(date) scheme_deactivation_periods.where("deactivation_date <= ?", date).order("created_at").last end def status @status ||= status_at(Time.zone.now) end def status_at(date) return :incomplete unless confirmed && locations.confirmed.any? return :deactivated if open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date :active end def active? status == :active end def reactivating_soon? status == :reactivating_soon end def deactivated? status == :deactivated end def deactivates_in_a_long_time? status_at(6.months.from_now) == :deactivating_soon end end