@@ -38,18 +38,18 @@ def validate(self):
3838 def set_date (self ):
3939 if getdate (self .start_date ) < getdate (today ()):
4040 frappe .throw (_ ("Start Date cannot be before the current date" ))
41+
4142 # set the end date as start date + max(send after days) in campaign schedule
42- send_after_days = []
43- campaign = frappe .get_doc ("Campaign" , self .campaign_name )
44- for entry in campaign .get ("campaign_schedules" ):
45- send_after_days .append (entry .send_after_days )
46- try :
47- self .end_date = add_days (getdate (self .start_date ), max (send_after_days ))
48- except ValueError :
43+ campaign = frappe .get_cached_doc ("Campaign" , self .campaign_name )
44+ send_after_days = [entry .send_after_days for entry in campaign .get ("campaign_schedules" )]
45+
46+ if not send_after_days :
4947 frappe .throw (
5048 _ ("Please set up the Campaign Schedule in the Campaign {0}" ).format (self .campaign_name )
5149 )
5250
51+ self .end_date = add_days (getdate (self .start_date ), max (send_after_days ))
52+
5353 def validate_lead (self ):
5454 lead_email_id = frappe .db .get_value ("Lead" , self .recipient , "email_id" )
5555 if not lead_email_id :
@@ -77,58 +77,128 @@ def update_status(self):
7777 start_date = getdate (self .start_date )
7878 end_date = getdate (self .end_date )
7979 today_date = getdate (today ())
80+
8081 if start_date > today_date :
81- self . db_set ( "status" , "Scheduled" , update_modified = False )
82+ new_status = "Scheduled"
8283 elif end_date >= today_date :
83- self .db_set ("status" , "In Progress" , update_modified = False )
84- elif end_date < today_date :
85- self .db_set ("status" , "Completed" , update_modified = False )
84+ new_status = "In Progress"
85+ else :
86+ new_status = "Completed"
87+
88+ if self .status != new_status :
89+ self .db_set ("status" , new_status , update_modified = False )
8690
8791
8892# called through hooks to send campaign mails to leads
8993def send_email_to_leads_or_contacts ():
94+ today_date = getdate (today ())
95+
96+ # Get all active email campaigns in a single query
9097 email_campaigns = frappe .get_all (
91- "Email Campaign" , filters = {"status" : ("not in" , ["Unsubscribed" , "Completed" , "Scheduled" ])}
98+ "Email Campaign" ,
99+ filters = {"status" : "In Progress" },
100+ fields = ["name" , "campaign_name" , "email_campaign_for" , "recipient" , "start_date" , "sender" ],
92101 )
93- for camp in email_campaigns :
94- email_campaign = frappe .get_doc ("Email Campaign" , camp .name )
95- campaign = frappe .get_cached_doc ("Campaign" , email_campaign .campaign_name )
102+
103+ if not email_campaigns :
104+ return
105+
106+ # Process each email campaign
107+ for email_campaign in email_campaigns :
108+ try :
109+ campaign = frappe .get_cached_doc ("Campaign" , email_campaign .campaign_name )
110+ except frappe .DoesNotExistError :
111+ frappe .log_error (
112+ title = _ ("Email Campaign Error" ),
113+ message = _ ("Campaign {0} not found" ).format (email_campaign .campaign_name ),
114+ )
115+ continue
116+
117+ # Find schedules that match today
96118 for entry in campaign .get ("campaign_schedules" ):
97- scheduled_date = add_days (email_campaign .get ("start_date" ), entry .get ("send_after_days" ))
98- if scheduled_date == getdate (today ()):
99- send_mail (entry , email_campaign )
119+ try :
120+ scheduled_date = add_days (getdate (email_campaign .start_date ), entry .get ("send_after_days" ))
121+ if scheduled_date == today_date :
122+ send_mail (entry , email_campaign )
123+ except Exception :
124+ frappe .log_error (
125+ title = _ ("Email Campaign Send Error" ),
126+ message = _ ("Failed to send email for campaign {0} to {1}" ).format (
127+ email_campaign .name , email_campaign .recipient
128+ ),
129+ )
100130
101131
102132def send_mail (entry , email_campaign ):
103- recipient_list = []
104- if email_campaign .email_campaign_for == "Email Group" :
105- for member in frappe .db .get_list (
106- "Email Group Member" , filters = {"email_group" : email_campaign .get ("recipient" )}, fields = ["email" ]
107- ):
108- recipient_list .append (member ["email" ])
133+ campaign_for = email_campaign .get ("email_campaign_for" )
134+ recipient = email_campaign .get ("recipient" )
135+ sender_user = email_campaign .get ("sender" )
136+ campaign_name = email_campaign .get ("name" )
137+
138+ # Get recipient emails
139+ if campaign_for == "Email Group" :
140+ recipient_list = frappe .get_all (
141+ "Email Group Member" ,
142+ filters = {"email_group" : recipient , "unsubscribed" : 0 },
143+ pluck = "email" ,
144+ )
109145 else :
110- recipient_list .append (
111- frappe .db .get_value (
112- email_campaign .email_campaign_for , email_campaign .get ("recipient" ), "email_id"
146+ email_id = frappe .db .get_value (campaign_for , recipient , "email_id" )
147+ if not email_id :
148+ frappe .log_error (
149+ title = _ ("Email Campaign Error" ),
150+ message = _ ("No email found for {0} {1}" ).format (campaign_for , recipient ),
113151 )
152+ return
153+ recipient_list = [email_id ]
154+
155+ if not recipient_list :
156+ frappe .log_error (
157+ title = _ ("Email Campaign Error" ),
158+ message = _ ("No recipients found for campaign {0}" ).format (campaign_name ),
114159 )
160+ return
161+
162+ # Get email template and sender
163+ email_template = frappe .get_cached_doc ("Email Template" , entry .get ("email_template" ))
164+ sender = frappe .db .get_value ("User" , sender_user , "email" ) if sender_user else None
165+
166+ # Build context for template rendering
167+ if campaign_for != "Email Group" :
168+ context = {"doc" : frappe .get_doc (campaign_for , recipient )}
169+ else :
170+ # For email groups, use the email group document as context
171+ context = {"doc" : frappe .get_doc ("Email Group" , recipient )}
172+
173+ # Render template
174+ subject = frappe .render_template (email_template .get ("subject" ), context )
175+ content = frappe .render_template (email_template .response_ , context )
176+
177+ try :
178+ comm = make (
179+ doctype = "Email Campaign" ,
180+ name = campaign_name ,
181+ subject = subject ,
182+ content = content ,
183+ sender = sender ,
184+ recipients = recipient_list ,
185+ communication_medium = "Email" ,
186+ sent_or_received = "Sent" ,
187+ send_email = False ,
188+ email_template = email_template .name ,
189+ )
190+
191+ frappe .sendmail (
192+ recipients = recipient_list ,
193+ subject = subject ,
194+ content = content ,
195+ sender = sender ,
196+ communication = comm ["name" ],
197+ queue_separately = True ,
198+ )
199+ except Exception :
200+ frappe .log_error (title = "Email Campaign Failed." )
115201
116- email_template = frappe .get_doc ("Email Template" , entry .get ("email_template" ))
117- sender = frappe .db .get_value ("User" , email_campaign .get ("sender" ), "email" )
118- context = {"doc" : frappe .get_doc (email_campaign .email_campaign_for , email_campaign .recipient )}
119- # send mail and link communication to document
120- comm = make (
121- doctype = "Email Campaign" ,
122- name = email_campaign .name ,
123- subject = frappe .render_template (email_template .get ("subject" ), context ),
124- content = frappe .render_template (email_template .response_ , context ),
125- sender = sender ,
126- bcc = recipient_list ,
127- communication_medium = "Email" ,
128- sent_or_received = "Sent" ,
129- send_email = True ,
130- email_template = email_template .name ,
131- )
132202 return comm
133203
134204
@@ -140,7 +210,12 @@ def unsubscribe_recipient(unsubscribe, method):
140210
141211# called through hooks to update email campaign status daily
142212def set_email_campaign_status ():
143- email_campaigns = frappe .get_all ("Email Campaign" , filters = {"status" : ("!=" , "Unsubscribed" )})
144- for entry in email_campaigns :
145- email_campaign = frappe .get_doc ("Email Campaign" , entry .name )
213+ email_campaigns = frappe .get_all (
214+ "Email Campaign" ,
215+ filters = {"status" : ("!=" , "Unsubscribed" )},
216+ pluck = "name" ,
217+ )
218+
219+ for name in email_campaigns :
220+ email_campaign = frappe .get_doc ("Email Campaign" , name )
146221 email_campaign .update_status ()
0 commit comments