Class: AudienceMember

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/audience_member.rb

Overview

== Schema Information

Table name: audience_members
Database name: primary

id :integer not null, primary key
active :boolean default(TRUE), not null
email_address :string
reconciled :boolean default(FALSE), not null
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
customer_id :integer
audience_id :integer
updater_id :integer

Indexes

idx_audience_members_unique_customer (audience_id,customer_id) UNIQUE WHERE email_address IS NULL AND customer_id IS NOT NULL
idx_audience_members_unique_email (audience_id,email_address) UNIQUE WHERE email_address IS NOT NULL
index_audience_members_on_active (active)
index_audience_members_on_customer_id (customer_id)
index_audience_members_on_email_address (email_address)

Foreign Keys

fk_rails_... (customer_id => parties.id) ON DELETE => cascade
fk_rails_... (audience_id => audiences.id) ON DELETE => cascade

Constant Summary collapse

ACTIVITY_DELIVERY_STATES =

campaign_delivery states that mean an email was actually transmitted or
attempted — the bar for "has seen activity" that forces archival over
deletion. pending/queued/suppressed/duplicate/exception sent nothing.

%w[sent bounced].freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#customer_idObject (readonly)

Validates customer id, audience id.

Validations (if => proc { |s| s.audience&.customer? } ):

  • Presence
  • Uniqueness ({ scope: :audience_id, message: 'can only be present once on each audience' })


57
# File 'app/models/audience_member.rb', line 57

validates :customer_id, presence: true, uniqueness: { scope: :audience_id, message: 'can only be present once on each audience' }, if: proc { |s| s.audience&.customer? }

#email_addressObject (readonly)

audience is optional (and nil once a audience_member is archived/detached),
so guard the list-scoped validations with &. — a nil list has no scope to
enforce uniqueness against.

Validations (if => proc { |s| s.audience&.email? } ):

  • Email_format
  • Presence
  • Uniqueness ({ scope: :audience_id, message: 'can only be present once on each audience' })


55
# File 'app/models/audience_member.rb', line 55

validates :email_address, email_format: true, presence: true, uniqueness: { scope: :audience_id, message: 'can only be present once on each audience' }, if: proc { |s| s.audience&.email? }

#update_customerObject

Returns the value of attribute update_customer.



77
78
79
# File 'app/models/audience_member.rb', line 77

def update_customer
  @update_customer
end

Class Method Details

.activeActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are active. Active Record Scope

Returns:

See Also:



64
# File 'app/models/audience_member.rb', line 64

scope :active, -> { where(active: true) }

.archivedActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are archived. Active Record Scope

Returns:

See Also:



66
# File 'app/models/audience_member.rb', line 66

scope :archived, -> { where.not(archived_at: nil) }

.inactiveActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are inactive. Active Record Scope

Returns:

See Also:



65
# File 'app/models/audience_member.rb', line 65

scope :inactive, -> { where(active: false) }

.unarchivedActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are unarchived. Active Record Scope

Returns:

See Also:



67
# File 'app/models/audience_member.rb', line 67

scope :unarchived, -> { where(archived_at: nil) }

.with_campaign_activityActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are with campaign activity. Active Record Scope

Returns:

See Also:



69
# File 'app/models/audience_member.rb', line 69

scope :with_campaign_activity, -> { where(id: CampaignDelivery.where(state: ACTIVITY_DELIVERY_STATES).select(:audience_member_id)) }

.with_customerActiveRecord::Relation<AudienceMember>

A relation of AudienceMembers that are with customer. Active Record Scope

Returns:

See Also:



71
72
73
74
75
# File 'app/models/audience_member.rb', line 71

scope :with_customer, -> {
  joins("left outer JOIN contact_points on contact_points.id = (select max(id) from contact_points where contact_points.detail = audience_members.email_address)
		left outer JOIN parties contacts on contacts.id = contact_points.party_id and contacts.type = 'Contact'
		left outer JOIN parties customers on customers.id = coalesce(audience_members.customer_id, contacts.customer_id, contact_points.party_id)")
}

Instance Method Details

#activateObject

Activate.



93
94
95
# File 'app/models/audience_member.rb', line 93

def activate
  update(active: true)
end

#archive!Boolean

Retain the audience_member for record-keeping: stamp archived_at and detach it
from its list (the list is being removed; audience_id is nullable).
update_columns bypasses validations/callbacks — a detached audience_member has no
list to scope the email/customer uniqueness checks against.

Returns:

  • (Boolean)


121
122
123
# File 'app/models/audience_member.rb', line 121

def archive!
  update_columns(archived_at: Time.current, audience_id: nil)
end

#archived?Boolean

Returns whether this audience_member has been archived for record-keeping.

Returns:

  • (Boolean)

    whether this audience_member has been archived for record-keeping



111
112
113
# File 'app/models/audience_member.rb', line 111

def archived?
  archived_at.present?
end

#audienceAudience?

Returns the audience this record belongs to.

Returns:

  • (Audience, nil)

    the audience this record belongs to



36
# File 'app/models/audience_member.rb', line 36

belongs_to :audience, inverse_of: :audience_members, optional: true

#campaign_deliveriesActiveRecord::Relation<CampaignDelivery>

dependent: :destroy so a audience_member with NO delivery activity takes its
(pending/suppressed/…) deliveries with it on hard-delete. AudienceMembers WITH
activity are archived instead (archive_instead_of_destroy_if_activity, which
aborts the destroy before this cascade runs), so their deliveries are kept.

Returns:

See Also:



46
# File 'app/models/audience_member.rb', line 46

has_many :campaign_deliveries, dependent: :destroy

#campaignsActiveRecord::Relation<Campaign>

Returns the associated campaigns.

Returns:

  • (ActiveRecord::Relation<Campaign>)

    the associated campaigns



50
# File 'app/models/audience_member.rb', line 50

has_many :campaigns, through: :audience

#contact_pointsActiveRecord::Relation<ContactPoint>

Returns the associated contact points.

Returns:

  • (ActiveRecord::Relation<ContactPoint>)

    the associated contact points



48
# File 'app/models/audience_member.rb', line 48

has_many :contact_points, foreign_key: :detail, primary_key: :email_address

#customerCustomer?

Returns the customer this record belongs to.

Returns:

  • (Customer, nil)

    the customer this record belongs to



38
# File 'app/models/audience_member.rb', line 38

belongs_to :customer, optional: true

#deactivateObject

Deactivate.



98
99
100
# File 'app/models/audience_member.rb', line 98

def deactivate
  update(active: false)
end

#determined_customerObject

Determined customer.



156
157
158
# File 'app/models/audience_member.rb', line 156

def determined_customer
  customer || party_customer
end

#email_preferenceEmailPreference?

Returns the email preference this record belongs to.

Returns:



40
# File 'app/models/audience_member.rb', line 40

belongs_to :email_preference, optional: true, primary_key: :email, foreign_key: :email_address

#nameString

Returns the name of the record.

Returns:

  • (String)

    the name of the record



137
138
139
# File 'app/models/audience_member.rb', line 137

def name
  party&.full_name
end

#partyObject

Party.



142
143
144
# File 'app/models/audience_member.rb', line 142

def party
  contact_points.where.not(party_id: nil).first&.party
end

#party_customerObject

Party customer.



147
148
149
150
151
152
153
# File 'app/models/audience_member.rb', line 147

def party_customer
  if party.respond_to?(:customer)
    party.customer
  else
    party
  end
end

#populate_email_if_missingObject

Populate email if missing.



166
167
168
169
170
171
172
# File 'app/models/audience_member.rb', line 166

def populate_email_if_missing
  return if email_address.present?
  return unless audience&.email?
  return unless customer

  self.email_address = customer.email
end

#remove!Boolean

The safe "remove a audience_member" entry point: archive it for record-keeping if
it has delivery activity, otherwise hard-delete it (cascading its inert
pending/suppressed deliveries). A bare #destroy is blocked for audience_members
with activity (see prevent_hard_delete_with_activity), so callers that mean
"remove from the list" should use this.

Returns:

  • (Boolean)


132
133
134
# File 'app/models/audience_member.rb', line 132

def remove!
  seen_campaign_activity? ? archive! : destroy!
end

#seen_campaign_activity?Boolean

True once an email was actually transmitted to / attempted for this
audience_member (a sent or bounced delivery exists).

Returns:

  • (Boolean)


106
107
108
# File 'app/models/audience_member.rb', line 106

def seen_campaign_activity?
  campaign_deliveries.where(state: ACTIVITY_DELIVERY_STATES).exists?
end

#update_linked_contact_pointsObject

Update linked contact points.



161
162
163
# File 'app/models/audience_member.rb', line 161

def update_linked_contact_points
  ContactPoint.where(detail: email_address_was).update_all(detail: email_address)
end