Aggiunge il rilevamento di invio e apertura delle email di campagna.
Ogni destinatario mostra se la mail è partita e se è stata aperta, con conteggi in dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -124,6 +124,7 @@ module Catalog
|
||||
ACTIVITY_TYPES = {
|
||||
"note" => "Nota",
|
||||
"email_sent" => "Email inviata",
|
||||
"email_opened" => "Email aperta",
|
||||
"email_received" => "Email ricevuta",
|
||||
"call" => "Telefonata",
|
||||
"meeting" => "Meeting",
|
||||
|
||||
@@ -96,6 +96,21 @@ class Mailing < ApplicationRecord
|
||||
mailing_recipients.failed.count
|
||||
end
|
||||
|
||||
def opened_count
|
||||
mailing_recipients.opened.count
|
||||
end
|
||||
|
||||
def variant_opened_count(variant)
|
||||
mailing_recipients.opened.where(ab_variant: variant).count
|
||||
end
|
||||
|
||||
def open_rate
|
||||
total = sent_count
|
||||
return 0 if total.zero?
|
||||
|
||||
((opened_count * 100.0) / total).round
|
||||
end
|
||||
|
||||
def queued_count
|
||||
mailing_recipients.queued.count
|
||||
end
|
||||
|
||||
@@ -5,12 +5,16 @@ class MailingRecipient < ApplicationRecord
|
||||
|
||||
validates :status, inclusion: { in: Catalog::MAILING_RECIPIENT_STATUSES.keys }
|
||||
validates :ab_variant, inclusion: { in: Catalog::AB_VARIANTS.keys }, allow_blank: true
|
||||
validates :tracking_token, presence: true, uniqueness: true
|
||||
|
||||
before_validation :assign_tracking_token, on: :create
|
||||
|
||||
scope :pending, -> { where(status: "pending") }
|
||||
scope :skipped, -> { where(status: "skipped") }
|
||||
scope :sent, -> { where(status: "sent") }
|
||||
scope :failed, -> { where(status: "failed") }
|
||||
scope :queued, -> { where(status: "queued") }
|
||||
scope :opened, -> { where.not(opened_at: nil) }
|
||||
scope :ordered, -> { joins(:organization).order("organizations.list_position ASC NULLS LAST", "organizations.name ASC") }
|
||||
|
||||
def status_label
|
||||
@@ -21,10 +25,33 @@ class MailingRecipient < ApplicationRecord
|
||||
status == "pending"
|
||||
end
|
||||
|
||||
def sent?
|
||||
status == "sent"
|
||||
end
|
||||
|
||||
def email_ok?
|
||||
email.present? && email.match?(URI::MailTo::EMAIL_REGEXP)
|
||||
end
|
||||
|
||||
def opened?
|
||||
opened_at.present?
|
||||
end
|
||||
|
||||
def record_open!(at: Time.current)
|
||||
return unless sent?
|
||||
|
||||
first_open = false
|
||||
with_lock do
|
||||
return unless sent?
|
||||
|
||||
first_open = opened_at.blank?
|
||||
attrs = { last_opened_at: at, open_count: open_count + 1 }
|
||||
attrs[:opened_at] = at if first_open
|
||||
update!(attrs)
|
||||
end
|
||||
log_open_activity! if first_open
|
||||
end
|
||||
|
||||
def merge_context
|
||||
opportunity = organization.campaign_opportunity(mailing.project)
|
||||
{
|
||||
@@ -90,6 +117,29 @@ class MailingRecipient < ApplicationRecord
|
||||
opportunity.update!(attrs) if attrs.any?
|
||||
end
|
||||
|
||||
def assign_tracking_token
|
||||
self.tracking_token ||= self.class.generate_tracking_token
|
||||
end
|
||||
|
||||
def self.generate_tracking_token
|
||||
loop do
|
||||
token = SecureRandom.urlsafe_base64(24)
|
||||
break token unless exists?(tracking_token: token)
|
||||
end
|
||||
end
|
||||
|
||||
def log_open_activity!
|
||||
organization.activities.create!(
|
||||
activity_type: "email_opened",
|
||||
subject: rendered_subject.presence || mailing.subject_for(ab_variant),
|
||||
description: "Apertura rilevata · campagna #{mailing.name}",
|
||||
happened_at: opened_at || Time.current,
|
||||
user: mailing.created_by,
|
||||
contact: contact,
|
||||
opportunity: organization.campaign_opportunity(mailing.project)
|
||||
)
|
||||
end
|
||||
|
||||
def log_activity!(subject_line)
|
||||
variant_note = "variante #{ab_variant}" if mailing.ab_test? && ab_variant.present?
|
||||
organization.activities.create!(
|
||||
|
||||
Reference in New Issue
Block a user