Files
eminuxCRM/app/models/mailing_recipient.rb
T
eminuxandCursor c4e5f289cf
CI / scan_ruby (push) Failing after 11m20s
CI / scan_js (push) Successful in 10m35s
CI / lint (push) Has been cancelled
Commit iniziale di eminuxCRM: CRM Rails con pipeline, campagne email e Docker.
Include autenticazione, progetti isolati, mail marketing HTML con SMTP, test A/B e editor WYSIWYG.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 23:01:48 +02:00

99 lines
3.1 KiB
Ruby

class MailingRecipient < ApplicationRecord
belongs_to :mailing
belongs_to :organization
belongs_to :contact, optional: true
validates :status, inclusion: { in: Catalog::MAILING_RECIPIENT_STATUSES.keys }
validates :ab_variant, inclusion: { in: Catalog::AB_VARIANTS.keys }, allow_blank: true
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 :ordered, -> { joins(:organization).order("organizations.list_position ASC NULLS LAST", "organizations.name ASC") }
def status_label
Catalog.label_for(Catalog::MAILING_RECIPIENT_STATUSES, status)
end
def pending?
status == "pending"
end
def email_ok?
email.present? && email.match?(URI::MailTo::EMAIL_REGEXP)
end
def merge_context
opportunity = organization.campaign_opportunity(mailing.project)
{
organization: organization,
contact: contact,
project: mailing.project,
opportunity: opportunity,
ab_variant: ab_variant.presence || opportunity&.ab_variant
}
end
def rendered_html
MailMerge.render(mailing.body_for(ab_variant), **merge_context)
end
def rendered_subject_line
MailMerge.render(mailing.subject_for(ab_variant), **merge_context)
end
def deliver!
with_lock do
return if status.in?(%w[sent skipped])
update!(status: "queued")
end
html = rendered_html
subject_line = rendered_subject_line
CampaignMailer.outreach(self, html: html, subject: subject_line).deliver_now
record_success!(subject_line)
rescue StandardError => e
update!(status: "failed", error_message: e.message.to_s.truncate(500))
ensure
mailing.mark_finished_if_done!
end
private
def record_success!(subject_line)
transaction do
update!(status: "sent", sent_at: Time.current, rendered_subject: subject_line, error_message: nil)
sync_opportunity!
log_activity!(subject_line)
end
end
def sync_opportunity!
opportunity = organization.campaign_opportunity(mailing.project)
return unless opportunity
attrs = {}
attrs[:send_status] = "sent" if opportunity.send_status == "to_send"
attrs[:sent_on] ||= Date.current if opportunity.sent_on.blank?
attrs[:pipeline_stage] = "contacted" if opportunity.pipeline_stage == "to_contact"
attrs[:ab_variant] = ab_variant if mailing.ab_test? && ab_variant.present? && opportunity.ab_variant.blank?
opportunity.update!(attrs) if attrs.any?
end
def log_activity!(subject_line)
variant_note = "variante #{ab_variant}" if mailing.ab_test? && ab_variant.present?
organization.activities.create!(
activity_type: "email_sent",
subject: subject_line.presence || mailing.name,
description: ["Campagna email: #{mailing.name}", variant_note].compact.join(" · "),
happened_at: Time.current,
user: mailing.created_by,
contact: contact,
opportunity: organization.campaign_opportunity(mailing.project)
)
end
end