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>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
class CampaignImportMatchlivetvLaunchTest < ActiveSupport::TestCase
|
||||
test "imports campaign rows onto MatchLiveTV" do
|
||||
csv = Tempfile.new(["campagna", ".csv"])
|
||||
csv.write(<<~CSV)
|
||||
N.,Regione,Prov.,Società,M/F,Email verificata,Sito / profilo,Streaming rilevato,Evidenza / fit commerciale,Test A/B,Stato invio,Data invio,Esito,Demo / Trial,Conversione,Piano,Fonte contatto / ricerca,Data verifica,Note follow-up
|
||||
1,Piemonte,TO,Vol-Ley Academy Volpiano,F,volley.academy.to@gmail.com,https://www.facebook.com/p/Vol-Ley-Academy-100063526780569/,NON RILEVATO,Academy giovanile,A,DA INVIARE,,,NO,NO,,https://example.com/fonte,2026-08-17,
|
||||
7,Lombardia,MB,Volley Brianza Est,F,ds@volleybrianzaest.it,https://www.volleybrianzaest.it/,SÌ / PARZIALE,U16/U18 nazionale,A,DA INVIARE,,,NO,NO,,https://www.volleybrianzaest.it/,46251,
|
||||
100,Valle d'Aosta,AO,Cogne Aosta Volley,F,cogneaostavolley@gmail.com,https://www.youtube.com/@CogneAostaVolley,SÌ – SPORTCAM,Vivaio prolifico,B,DA INVIARE,,,NO,NO,,https://www.fipav-vda.com/societa,2026-08-17,
|
||||
CSV
|
||||
csv.flush
|
||||
|
||||
result = CampaignImport::MatchlivetvLaunch.new(
|
||||
path: csv.path,
|
||||
user: users(:admin),
|
||||
project: projects(:matchlivetv),
|
||||
wipe: false
|
||||
).call
|
||||
|
||||
assert_empty result.errors, result.errors.inspect
|
||||
assert_equal 3, result.imported
|
||||
|
||||
org = Organization.find_by!(name: "Vol-Ley Academy Volpiano")
|
||||
assert_equal "female", org.team_gender
|
||||
assert_equal "not_detected", org.streaming_status
|
||||
assert_equal 1, org.list_position
|
||||
assert_equal Date.new(2026, 8, 17), org.verified_at
|
||||
assert_equal "campaign", org.lead_source
|
||||
assert org.projects.exists?(code: "matchlivetv")
|
||||
assert_equal "volley.academy.to@gmail.com", org.contacts.primary_first.first.email
|
||||
|
||||
opp = org.campaign_opportunity(projects(:matchlivetv))
|
||||
assert_equal "A", opp.ab_variant
|
||||
assert_equal "to_send", opp.send_status
|
||||
assert_equal "to_contact", opp.pipeline_stage
|
||||
assert_not opp.demo_trial
|
||||
assert_not opp.converted
|
||||
|
||||
brianza = Organization.find_by!(name: "Volley Brianza Est")
|
||||
assert_equal "yes_partial", brianza.streaming_status
|
||||
assert_equal Date.new(2026, 8, 17), brianza.verified_at
|
||||
|
||||
cogne = Organization.find_by!(name: "Cogne Aosta Volley")
|
||||
assert_equal "yes_sportcam", cogne.streaming_status
|
||||
assert_equal "B", cogne.campaign_opportunity(projects(:matchlivetv)).ab_variant
|
||||
ensure
|
||||
csv.close!
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,36 @@
|
||||
require "test_helper"
|
||||
|
||||
class MailMergeTest < ActiveSupport::TestCase
|
||||
test "replaces tokens from organization and contact" do
|
||||
html = MailMerge.render(
|
||||
"Ciao {{contatto_nome}} di {{societa}} in {{regione}}",
|
||||
organization: organizations(:acme),
|
||||
contact: contacts(:primary),
|
||||
project: projects(:matchlivetv),
|
||||
opportunity: opportunities(:deal)
|
||||
)
|
||||
|
||||
assert_equal "Ciao Mario Rossi di ASD Test Calcio in Lombardia", html
|
||||
end
|
||||
|
||||
test "falls back to primary contact and org email" do
|
||||
vars = MailMerge.variables_for(organization: organizations(:acme), project: projects(:matchlivetv))
|
||||
assert_equal "Mario Rossi", vars["contatto_nome"]
|
||||
assert_equal "mario@asdtest.example.it", vars["email"]
|
||||
assert_equal "MatchLiveTV", vars["progetto"]
|
||||
assert_equal "Full", vars["piano"]
|
||||
end
|
||||
|
||||
test "test_ab prefers explicit variant" do
|
||||
vars = MailMerge.variables_for(
|
||||
organization: organizations(:acme),
|
||||
project: projects(:matchlivetv),
|
||||
ab_variant: "B"
|
||||
)
|
||||
assert_equal "B", vars["test_ab"]
|
||||
end
|
||||
|
||||
test "unknown tokens become empty string" do
|
||||
assert_equal "X Y", MailMerge.render("X {{sconosciuto}} Y")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
require "test_helper"
|
||||
|
||||
class MailingsRecipientBuilderTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
opportunities(:deal).update!(send_status: "to_send", ab_variant: "A")
|
||||
end
|
||||
|
||||
test "audience to_send includes only pending campaign orgs with valid email" do
|
||||
mailing = create_mailing(audience: "to_send")
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
recipient = mailing.mailing_recipients.find_by!(organization: organizations(:acme))
|
||||
assert_equal "pending", recipient.status
|
||||
assert_equal "mario@asdtest.example.it", recipient.email
|
||||
end
|
||||
|
||||
test "skips organizations without email" do
|
||||
org = organizations(:acme)
|
||||
org.update!(email: nil)
|
||||
org.contacts.update_all(email: nil)
|
||||
|
||||
mailing = create_mailing(audience: "all")
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
recipient = mailing.mailing_recipients.find_by!(organization: org)
|
||||
assert_equal "skipped", recipient.status
|
||||
assert_equal "manca email", recipient.skip_reason
|
||||
end
|
||||
|
||||
test "audience test_a filters by variant" do
|
||||
mailing = create_mailing(audience: "test_a")
|
||||
mailing.rebuild_recipients!
|
||||
assert_equal 1, mailing.mailing_recipients.count
|
||||
|
||||
mailing.update!(audience: "test_b")
|
||||
mailing.rebuild_recipients!
|
||||
assert_equal 0, mailing.mailing_recipients.count
|
||||
end
|
||||
|
||||
test "ab test from_record uses opportunity variant" do
|
||||
create_campaign_org(name: "Beta Volley", email: "beta@example.com", ab_variant: "B", list_position: 2)
|
||||
mailing = create_mailing(
|
||||
audience: "all",
|
||||
ab_test: true,
|
||||
ab_assignment: "from_record",
|
||||
subject_b: "Offerta B {{societa}}",
|
||||
body_html_b: "<p>Versione B</p>"
|
||||
)
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
acme = mailing.mailing_recipients.find_by!(organization: organizations(:acme))
|
||||
beta = mailing.mailing_recipients.joins(:organization).find_by!(organizations: { name: "Beta Volley" })
|
||||
assert_equal "A", acme.ab_variant
|
||||
assert_equal "B", beta.ab_variant
|
||||
assert_match(/Ciao Mario Rossi/, acme.rendered_html)
|
||||
assert_equal "Offerta B Beta Volley", beta.rendered_subject_line
|
||||
assert_match(/Versione B/, beta.rendered_html)
|
||||
end
|
||||
|
||||
test "ab test split alternates A and B" do
|
||||
create_campaign_org(name: "Beta Volley", email: "beta@example.com", ab_variant: "A", list_position: 2)
|
||||
organizations(:acme).update!(list_position: 1)
|
||||
mailing = create_mailing(
|
||||
audience: "all",
|
||||
ab_test: true,
|
||||
ab_assignment: "split",
|
||||
subject_b: "B {{societa}}",
|
||||
body_html_b: "<p>B</p>"
|
||||
)
|
||||
mailing.rebuild_recipients!
|
||||
|
||||
variants = mailing.mailing_recipients.pending.ordered.map(&:ab_variant)
|
||||
assert_equal %w[A B], variants
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user