class Log < ApplicationRecord include CollectionTimeHelper self.abstract_class = true belongs_to :owning_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 before_save :update_status! STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2, "pending" => 3, "deleted" => 4, }.freeze enum status: STATUS enum status_cache: STATUS, _prefix: true scope :visible, -> { where(status: %w[not_started in_progress completed]) } scope :exportable, -> { where(status: %w[not_started in_progress completed deleted]) } scope :filter_by_status, ->(status, _user = nil) { where status: } scope :filter_by_years, lambda { |years, _user = nil| first_year = years.shift query = filter_by_year(first_year) years.each { |year| query = query.or(filter_by_year(year)) } query.all } scope :filter_by_postcode, ->(postcode_full) { where("REPLACE(postcode_full, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") } scope :filter_by_id, ->(id) { where(id:) } scope :filter_by_user, lambda { |selected_user, user| if !selected_user.include?("all") && user.present? where(created_by: user) end } scope :filter_by_bulk_upload_id, lambda { |bulk_upload_id, user| joins(:bulk_upload) .where(bulk_upload: { id: bulk_upload_id, user: }) } scope :created_by, ->(user) { where(created_by: user) } attr_accessor :skip_update_status, :skip_update_uprn_confirmed def process_uprn_change! if uprn.present? service = UprnClient.new(uprn) service.call return errors.add(:uprn, :uprn_error, message: service.error) if service.error.present? presenter = UprnDataPresenter.new(service.result) self.uprn_known = 1 self.uprn_confirmed = nil unless skip_update_uprn_confirmed self.address_line1 = presenter.address_line1 self.address_line2 = presenter.address_line2 self.town_or_city = presenter.town_or_city self.postcode_full = presenter.postcode self.county = nil process_postcode_changes! end end def collection_start_year return @start_year if @start_year return unless startdate window_end_date = Time.zone.local(startdate.year, 4, 1) @start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year end def recalculate_start_year! @start_year = nil collection_start_year end def lettings? false end def sales? false end def ethnic_refused? ethnic_group == 17 end def collection_period_open? return false if older_than_previous_collection_year? form.new_logs_end_date > Time.zone.today end def collection_period_open_for_editing? return false if older_than_previous_collection_year? form.edit_end_date > Time.zone.today end def blank_invalid_non_setup_fields! setup_ids = form.setup_sections.flat_map(&:subsections).flat_map(&:questions).map(&:id) 2.times do next if valid? errors.each do |error| next if setup_ids.include?(error.attribute.to_s) question = form.questions.find { |q| q.id == error.attribute.to_s } if question&.type == "checkbox" question.answer_keys_without_dividers.each { |attribute| public_send("#{attribute}=", nil) } else public_send("#{error.attribute}=", nil) end end blank_compound_invalid_non_setup_fields! errors.clear end end (1..8).each do |person_num| define_method("retirement_age_for_person_#{person_num}") do retirement_age_for_person(person_num) end define_method("plural_gender_for_person_#{person_num}") do plural_gender_for_person(person_num) end end (2..8).each do |person_num| define_method("person_#{person_num}_child_relation?") do send("relat#{person_num}") == "C" end end def discard! update!(status: "deleted", discarded_at: Time.zone.now) end def calculate_status return "deleted" if discarded_at.present? if all_subsections_completed? && errors.empty? "completed" elsif all_subsections_unstarted? "not_started" else "in_progress" end end def field_formatted_as_currency(field_name) field_value = public_send(field_name) format_as_currency(field_value) end def blank_compound_invalid_non_setup_fields! self.ppcodenk = nil if errors.attribute_names.include? :ppostcode_full if errors.of_kind?(:uprn, :uprn_error) self.uprn_known = nil self.uprn_confirmed = nil self.address_line1 = nil self.address_line2 = nil self.town_or_city = nil self.postcode_full = nil self.county = nil process_postcode_changes! end end def creation_method bulk_uploaded? ? "bulk upload" : "single log" end def bulk_uploaded? bulk_upload_id.present? end def collection_closed_for_editing? form.edit_end_date < Time.zone.now || older_than_previous_collection_year? end private # Handle logs that are older than previous collection start date def older_than_previous_collection_year? return false unless startdate startdate < previous_collection_start_date end def plural_gender_for_person(person_num) gender = public_send("sex#{person_num}".to_sym) return unless gender if %w[M X].include?(gender) "male and non-binary people" elsif gender == "F" "females" end end def update_status! return if skip_update_status self.status = calculate_status end def all_subsections_completed? form.subsections.all? { |subsection| subsection.complete?(self) || subsection.not_displayed_in_tasklist?(self) } end def all_subsections_unstarted? not_started_statuses = %i[not_started cannot_start_yet] form.subsections.all? { |subsection| not_started_statuses.include? subsection.status(self) } end def reset_invalidated_dependent_fields! return unless form form.reset_not_routed_questions_and_invalid_answers(self) reset_created_by! end PIO = PostcodeService.new def process_previous_postcode_changes! self.ppostcode_full = upcase_and_remove_whitespace(ppostcode_full) process_postcode(ppostcode_full, "ppcodenk", "is_previous_la_inferred", "prevloc") end LA_CHANGES = { "E07000027" => "E06000064", # Barrow-in-Furness => Westmorland and Furness "E07000030" => "E06000064", # Eden => Westmorland and Furness "E07000031" => "E06000064", # South Lakeland => Westmorland and Furness "E07000026" => "E06000063", # Allerdale => Cumberland "E07000028" => "E06000063", # Carlisle => Cumberland "E07000029" => "E06000063", # Copeland => Cumberland "E07000163" => "E06000065", # Craven => North Yorkshire "E07000164" => "E06000065", # Hambleton => North Yorkshire "E07000165" => "E06000065", # Harrogate => North Yorkshire "E07000166" => "E06000065", # Richmondshire => North Yorkshire "E07000167" => "E06000065", # Ryedale => North Yorkshire "E07000168" => "E06000065", # Scarborough => North Yorkshire "E07000169" => "E06000065", # Selby => North Yorkshire "E07000187" => "E06000066", # Mendip => Somerset "E07000188" => "E06000066", # Sedgemoor => Somerset "E07000246" => "E06000066", # Somerset West and Taunton => Somerset "E07000189" => "E06000066", # South Somerset => Somerset }.freeze def get_inferred_la(postcode) result = PIO.lookup(postcode) location_code = result[:location_code] if result if LA_CHANGES.key?(location_code) && form.start_date.year >= 2023 LA_CHANGES[location_code] elsif !(LA_CHANGES.value?(location_code) && form.start_date.year < 2023) location_code end end def upcase_and_remove_whitespace(string) string.present? ? string.upcase.gsub(/\s+/, "") : string end def reset_location_fields! reset_location(is_la_inferred, "la", "is_la_inferred", "postcode_full", 1) end def reset_previous_location_fields! reset_location(is_previous_la_inferred, "prevloc", "is_previous_la_inferred", "ppostcode_full", previous_la_known) end def reset_location(is_inferred, la_key, is_inferred_key, postcode_key, is_la_known) if is_inferred || is_la_known != 1 self[la_key] = nil end self[is_inferred_key] = false self[postcode_key] = nil end end