La scelta della lista passa da un wizard a tre passi, con anteprima del conteggio e lista congelata solo dopo la conferma. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
5.8 KiB
Ruby
154 lines
5.8 KiB
Ruby
module Mailings
|
|
class AudienceQuery
|
|
def initialize(project, filters = {}, mailing: nil)
|
|
@project = project
|
|
@filters = filters.is_a?(AudienceFilters) ? filters : AudienceFilters.new(filters)
|
|
@mailing = mailing
|
|
end
|
|
|
|
def relation
|
|
scope = Organization.for_project(@project)
|
|
scope = apply_preset(scope)
|
|
scope = apply_organization_filters(scope)
|
|
scope = apply_opportunity_filters(scope)
|
|
scope = apply_history_filters(scope)
|
|
scope = apply_exclusions(scope)
|
|
scope.distinct
|
|
end
|
|
|
|
def counts
|
|
{
|
|
total: relation.unscope(:order).distinct.count("organizations.id"),
|
|
with_email: with_email_scope(relation).unscope(:order).distinct.count("organizations.id")
|
|
}
|
|
end
|
|
|
|
def preview(limit = 15)
|
|
relation.includes(:primary_contact).reorder(Arel.sql("organizations.list_position ASC NULLS LAST"), "organizations.name ASC").limit(limit)
|
|
end
|
|
|
|
private
|
|
|
|
def apply_preset(scope)
|
|
case @filters.preset
|
|
when "to_send"
|
|
scope.where(id: opportunity_scope.where(send_status: "to_send").select(:organization_id))
|
|
when "test_a"
|
|
scope.where(id: opportunity_scope.where(ab_variant: "A").select(:organization_id))
|
|
when "test_b"
|
|
scope.where(id: opportunity_scope.where(ab_variant: "B").select(:organization_id))
|
|
else
|
|
scope
|
|
end
|
|
end
|
|
|
|
def apply_organization_filters(scope)
|
|
scope = scope.where(sport: @filters["sports"]) if @filters["sports"].present?
|
|
scope = scope.where(team_gender: @filters["team_genders"]) if @filters["team_genders"].present?
|
|
scope = scope.where(region: @filters["regions"]) if @filters["regions"].present?
|
|
scope = scope.where(province: @filters["provinces"]) if @filters["provinces"].present?
|
|
scope = scope.where(streaming_status: @filters["streaming_statuses"]) if @filters["streaming_statuses"].present?
|
|
scope = scope.where(status: @filters["statuses"]) if @filters["statuses"].present?
|
|
scope = scope.where("organizations.list_position >= ?", @filters["list_min"]) if @filters["list_min"].present?
|
|
scope = scope.where("organizations.list_position <= ?", @filters["list_max"]) if @filters["list_max"].present?
|
|
scope
|
|
end
|
|
|
|
def apply_opportunity_filters(scope)
|
|
extra = opportunity_scope
|
|
filtered = false
|
|
if @filters["send_statuses"].present?
|
|
extra = extra.where(send_status: @filters["send_statuses"])
|
|
filtered = true
|
|
end
|
|
if @filters["ab_variants"].present?
|
|
extra = extra.where(ab_variant: @filters["ab_variants"])
|
|
filtered = true
|
|
end
|
|
if @filters["pipeline_stages"].present?
|
|
extra = extra.where(pipeline_stage: @filters["pipeline_stages"])
|
|
filtered = true
|
|
end
|
|
case @filters["estimated_value"]
|
|
when "present"
|
|
extra = extra.where("opportunities.estimated_value IS NOT NULL AND opportunities.estimated_value > 0")
|
|
filtered = true
|
|
when "blank"
|
|
extra = extra.where("opportunities.estimated_value IS NULL OR opportunities.estimated_value = 0")
|
|
filtered = true
|
|
end
|
|
|
|
filtered ? scope.where(id: extra.select(:organization_id)) : scope
|
|
end
|
|
|
|
def apply_history_filters(scope)
|
|
case @filters.history_kind
|
|
when "never"
|
|
scope.where.not(id: sent_org_ids)
|
|
when "received"
|
|
scope.where(id: sent_org_ids(mailing_id: @filters.history_mailing_id))
|
|
when "not_received"
|
|
scope.where.not(id: sent_org_ids(mailing_id: @filters.history_mailing_id))
|
|
when "opened"
|
|
scope.where(id: opened_org_ids(mailing_id: @filters.history_mailing_id))
|
|
when "not_opened"
|
|
sent = sent_org_ids(mailing_id: @filters.history_mailing_id)
|
|
opened = opened_org_ids(mailing_id: @filters.history_mailing_id)
|
|
scope.where(id: sent).where.not(id: opened)
|
|
when "recent"
|
|
days = @filters.history_days.presence || 30
|
|
scope.where(id: sent_org_ids(since: days.days.ago))
|
|
else
|
|
scope
|
|
end
|
|
end
|
|
|
|
def apply_exclusions(scope)
|
|
scope = scope.where.not(status: %w[active_customer inactive_customer]) if @filters["exclude_customers"]
|
|
if @filters["exclude_received_mailing_ids"].present?
|
|
scope = scope.where.not(id: sent_org_ids(mailing_id: project_mailing_ids(@filters["exclude_received_mailing_ids"])))
|
|
end
|
|
if @filters["exclude_opened_mailing_ids"].present?
|
|
scope = scope.where.not(id: opened_org_ids(mailing_id: project_mailing_ids(@filters["exclude_opened_mailing_ids"])))
|
|
end
|
|
if @filters["exclude_mailed_within_days"].present?
|
|
scope = scope.where.not(id: sent_org_ids(since: @filters["exclude_mailed_within_days"].days.ago))
|
|
end
|
|
scope
|
|
end
|
|
|
|
def with_email_scope(scope)
|
|
scope.left_joins(:contacts).where(
|
|
"NULLIF(BTRIM(organizations.email), '') IS NOT NULL OR NULLIF(BTRIM(contacts.email), '') IS NOT NULL"
|
|
)
|
|
end
|
|
|
|
def opportunity_scope
|
|
Opportunity.where(project_id: @project.id)
|
|
end
|
|
|
|
def sent_org_ids(mailing_id: nil, since: nil)
|
|
recips = history_recipients.where(status: "sent")
|
|
recips = recips.where(mailing_id: mailing_id) if mailing_id.present?
|
|
recips = recips.where("mailing_recipients.sent_at >= ?", since) if since.present?
|
|
recips.select(:organization_id)
|
|
end
|
|
|
|
def opened_org_ids(mailing_id: nil)
|
|
recips = history_recipients.where.not(opened_at: nil)
|
|
recips = recips.where(mailing_id: mailing_id) if mailing_id.present?
|
|
recips.select(:organization_id)
|
|
end
|
|
|
|
def history_recipients
|
|
recips = MailingRecipient.joins(:mailing).where(mailings: { project_id: @project.id })
|
|
recips = recips.where.not(mailing_id: @mailing.id) if @mailing&.persisted?
|
|
recips
|
|
end
|
|
|
|
def project_mailing_ids(ids)
|
|
Mailing.for_project(@project).where(id: ids).select(:id)
|
|
end
|
|
end
|
|
end
|