Class: PartyResearchFinding

Inherits:
ApplicationRecord show all
Defined in:
app/models/party_research_finding.rb

Overview

One proposed change discovered by an adapter during a research run.
Findings start as pending and a CRM user reviews each one individually
(Ancestry-style hint UI), accepting or rejecting it. Re-running enrichment
on the same party flips any still-pending older findings to superseded
so the UI never shows stale duplicates.

The apply step (writing accepted findings back to Party / EmailAddress /
PhoneNumber / Address / Activity / Contact) is gated by a category-level
allowlist enforced at the DB layer (the category column is a Postgres
enum) and a field-level allowlist enforced at the app layer.

Constant Summary collapse

STATES =
%w[pending accepted rejected superseded].freeze
CATEGORIES =

industry is in the PG enum (legacy from the initial migration) but
no adapter emits it — the profile_id mapping superseded it. The
enum value stays for historical safety; the documented set here is
only what we actively emit.

%w[name email phone address profile note contact_suggestion social_profile inactive].freeze
SOCIAL_PLATFORMS =

ContactPoint categories that a social_profile finding can map onto
when applied. Anything else (youtube, github, tiktok, etc.) falls
back to ContactPoint's generic website category.

%w[website facebook twitter pinterest linkedin instagram houzz].freeze

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Has one collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#adapterObject (readonly)



34
# File 'app/models/party_research_finding.rb', line 34

validates :adapter, presence: true

#confidenceObject (readonly)



35
# File 'app/models/party_research_finding.rb', line 35

validates :confidence, numericality: { in: 0..100, allow_nil: true }

Class Method Details

.pendingActiveRecord::Relation<PartyResearchFinding>

A relation of PartyResearchFindings that are pending. Active Record Scope

Returns:

See Also:



31
# File 'app/models/party_research_finding.rb', line 31

scope :pending, -> { where(state: 'pending') }

.reviewedActiveRecord::Relation<PartyResearchFinding>

A relation of PartyResearchFindings that are reviewed. Active Record Scope

Returns:

See Also:



32
# File 'app/models/party_research_finding.rb', line 32

scope :reviewed, -> { where.not(reviewed_at: nil) }

Instance Method Details

#partyParty

Returns:

See Also:



17
# File 'app/models/party_research_finding.rb', line 17

has_one :party, through: :party_research_run

#party_research_runPartyResearchRun



14
# File 'app/models/party_research_finding.rb', line 14

belongs_to :party_research_run

#reviewed_byAccount

Returns:

See Also:



15
# File 'app/models/party_research_finding.rb', line 15

belongs_to :reviewed_by, class_name: 'Account', optional: true

#status_against_partyObject

What will happen if a reviewer clicks "Accept" on this finding,
given the Party's current data? Drives the badge + button label in
the review UI so reviewers see "already on file" vs "new" vs
"change" upfront instead of guessing.

Returns one of: :already_on_file, :new, :change, :update_existing,
:incomplete, or nil when the category doesn't have a comparable
current value (contact_suggestion).



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'app/models/party_research_finding.rb', line 45

def status_against_party
  case category
  when 'phone'          then phone_on_party? ? :already_on_file : :new
  when 'email'          then email_on_party? ? :already_on_file : :new
  when 'address'
    if address_on_party? then :already_on_file
    elsif address_missing_street? then :incomplete
    else :new
    end
  when 'name'           then names_equal? ? :already_on_file : :update_existing
  when 'profile'        then profile_unchanged? ? :already_on_file : :change
  when 'social_profile' then social_profile_on_party? ? :already_on_file : :new
  when 'note'
    if note_on_party? then :already_on_file
    elsif party.profile_info.to_s.strip.empty? then :new
    else :update_existing
    end
  when 'inactive'
    party.inactive? ? :already_on_file : :deactivate
  end
end