You need to sign in to do that
Don't have an account?

Sending email from scheduled apex.
Hi Everyone,
I've run into an 'out of the box' problem that I could do with someone advice solving.
The situation
There's a third party system that inserts new order records (custom object) into salesforce through the API. The third party system may or may not insert one or more payment instruction records (a master-detail object of order) 90 seconds after the order is inserted. I've been asked to make a PDF order confirmation that gets emailed to the customer automatically when one of these orders is received.
The problem
The confirmation email needs to include payment instructions that we've received, but orders don't require payment instructions. Sometimes we get orders with no instructions initially. We might get those instructions much later on, but we still need to send a confirmation. This has two implications:
- I can't trigger this email from the order object because we haven't got the payment instructions by then so all confirmations will appear as if we haven't received the instructions.
- I can't trigger this email from the payment instruction object because if an order doesn't have payment instructions, we won't send a confirmation at all.
A failed solution
I thought I'd be smart and set up a scheduled apex task that runs every 5 minutes, and collects all orders, older than three minutes that haven't had a confirmation sent (using a checkbox to indicate whether the confirmation has been sent or not). This sounded like a decent hack on paper, but then I found out you can't send emails from scheduled apex. This fact alone has thrown my only remaining option on the fire.
Are there any ideas out there on how I could solve this problem?
Hi Greg,
I actually stumbled upon the same article and managed to make it work for me. I wasn't going to post my solution so hastily this time in case it didn't work.
Basically, I had a visualforce page that generated a PDF, and wanted to include this in an email. Previously, I had used the PageReference and passed the id of the record I wanted to build the PDF from as a parameter. You can include a visualforce page in a visualforce email template, but you can't pass parameters to the page and an email template doesn't have a controller that it will pass to your pages constructor. This is why it wasn't going to work, and I imagine why you're having the same difficulty.
The solution is to use a component instead of a page for the PDF body. A component let's you pass custom attributes to the components controller when you call it. Here's a working example. I've omitted things specific to my use case.
Component Controller
Note that dealId is get/set variable. Also note that when I get the deal object, I first check if it's null, and have to query for it if it is. This is because you can't do the query on the set method of the ID variable, and this seemed like the most reliable alternative.
PDF Body Component
Note the attribute, you'll see how it's used int the email template.
VisualForce Email Template
Note that when you call the component, you get to pass in attributes.
Plain Old PDF Page
Now that we've got the PDF working in the email, to save doubling our efforts by maintaing a PDF page as well, I'm going to write a page that renders as PDF, but only includes the PDF Body component.
Another way of doing things:
My solution is based on passing the deal ID to the component, and letting the component query the information it needs, but there is another way. You can pass objects through attributes as well, so I was able to pass {!relatedTo} as the value of a Deal__c attribute, and my controller simply stored the deal in it's deal get/set variable.
This is arguably, much simpler, but it meant that whereever I used the component I needed to make sure I queried the deal with the correct fields before invoking the component. For simplicity sake, I'm going to let my component take care of all its own queries so I can be 100% sure it has what it needs.
All Answers
I think I've figure out a potential solution. Going to test it tonight.
1. Make trigger on order insert that generates the pdf and saves as an attachment on the order.
2. Make trigger on payment instruction that generates a new pdf and over writes the existing pdf generated by the order insert trigger.
3. Schedule some apex that runs every 5 minutes, and sends an email using the pdf that's attached to the order, instead of trying to generate the pdf in the scheduled apex code.
This should work around the limitation of not being able to use getContent or getContentAsPDF in scheduled or future methods.
Bah! Humbug!
This didn't work. I'm desperate to solve this problem now.
Hello.
I'm having a similar issue, where I'm looking to generate a PDF at a set interval (scheduled). The limitations of the scheduler in not being able to use the getContent methods is making things difficult.
There's a method posted at the following link that provides a possible workaround: http://wiki.developerforce.com/index.php/Visualforce_EmailQuote2PDF
I'm unable to figure a way to get this method to work for my own purposes, though, as it seems that it will only work for standard controllers. I'm curious if you find a way to accomplish what you need.
-Greg
Hi Greg,
I actually stumbled upon the same article and managed to make it work for me. I wasn't going to post my solution so hastily this time in case it didn't work.
Basically, I had a visualforce page that generated a PDF, and wanted to include this in an email. Previously, I had used the PageReference and passed the id of the record I wanted to build the PDF from as a parameter. You can include a visualforce page in a visualforce email template, but you can't pass parameters to the page and an email template doesn't have a controller that it will pass to your pages constructor. This is why it wasn't going to work, and I imagine why you're having the same difficulty.
The solution is to use a component instead of a page for the PDF body. A component let's you pass custom attributes to the components controller when you call it. Here's a working example. I've omitted things specific to my use case.
Component Controller
Note that dealId is get/set variable. Also note that when I get the deal object, I first check if it's null, and have to query for it if it is. This is because you can't do the query on the set method of the ID variable, and this seemed like the most reliable alternative.
PDF Body Component
Note the attribute, you'll see how it's used int the email template.
VisualForce Email Template
Note that when you call the component, you get to pass in attributes.
Plain Old PDF Page
Now that we've got the PDF working in the email, to save doubling our efforts by maintaing a PDF page as well, I'm going to write a page that renders as PDF, but only includes the PDF Body component.
Another way of doing things:
My solution is based on passing the deal ID to the component, and letting the component query the information it needs, but there is another way. You can pass objects through attributes as well, so I was able to pass {!relatedTo} as the value of a Deal__c attribute, and my controller simply stored the deal in it's deal get/set variable.
This is arguably, much simpler, but it meant that whereever I used the component I needed to make sure I queried the deal with the correct fields before invoking the component. For simplicity sake, I'm going to let my component take care of all its own queries so I can be 100% sure it has what it needs.
Hi Logan.
Thank you very much. This worked perfectly! And, as you had mentioned, I replaced my standard PDF page with a simple container page that pulls in the component.
I had seen a couple of other threads on the forums that were attempting to accomplish the same thing. I'll post a reference to your posting here.
Thanks again.
-Greg
Now how are you sending email? Is it through batch apex or manually selecting VF template in standard email window?
Please let me know, as I am running into similar kind of problem and been trying out different logics.
Hi Abhi.
I'm sending it via Batch Apex. Basically, I loop on all of the members of a particular group that I want to send the message to, and send each an individual message. Here's my simplified code below, including a test method.
Hope that helps.
-Greg
This is how I wanted to kick off emails, but the most frequent you can schedule a task to occur is once an hour.
Instead, I wrote a web service method called "everyFiveMinutes" and I've got a python script on one of our linux servers thats run every five minutes by cron and will call the everyFiveMinutes method. My scheduled tasks are effectively offloaded to another machine :D.
Thanks for the reply.
Ok so basically you are selecting template. In my case I need to attach an excel to the mail. getContent() is not supported by scheduler :(
Hey, you have written sendEmail() method inside for loop. It will hit the limits (think its 10, i.e. you will be able to send 10 emails in one shot).
Hi Abhi.
Calling sendEmail inside the loop will definitely hit limits, and might not be considered a best practice. But for this particular case, I am only sending to a very small number of individuals in a group that I control. For larger sends, I would have to rethink this a bit.
In regards to attaching an Excel, I'm simply using renderas="pdf" in my messaging attachment and including the component. See below:
Have you tried using "application/x-excel" instead of "pdf" in the renderas?
Logan, nice solution!
-Greg
Thanks Greg for helping.
I really don't understand what I am missing, can you please look into this.
The VF email template
Th VF component:
And it's controller:
I checked it over and over, but couldn't find the mistake. The email is not received that is the only thing I could find.
Any help will be greatly appreciated, as this is very important for my implementation.
Hi Abhi.
I can't see anything obviously wrong with the code, but I do have some questions.
-Greg
Thanks Greg.
The ID I have specified is valid contact and Email Opt Out is not checked.
Here is the debug log:
The 'mail' content:
Messaging.SingleEmailMessage[getBccAddresses=null;getCcAddresses=null;getCharset=null;getDocumentAttachments=null;getFileAttachments=null;getHtmlBody=null;getInReplyTo=null;getOrgWideEmailAddressId=null;getPlainTextBody=null;getReferences=null;getTargetObjectId=003R000000YOmRwIAL;getTemplateId=00XR0000000IH56MAG;getToAddresses=(abhilash023+sctest_reseller2_1@gmail.com);getWhatId=003R000000YOmRwIAL;isUserMail=false;]
SendEmailResult 'r':
(Messaging.SendEmailResult[getErrors=(Messaging.SendEmailError[getTargetObjectId=null;]);isSuccess=false;], Messaging.SendEmailResult[getErrors=(Messaging.SendEmailError[getTargetObjectId=null;]);isSuccess=false;])
Also, when I select this template in UI, the PDF is just with table headers, no data in it !!
But when I use the component in VF page, the table is displayed related particular Account (or Reseller).
Now I am able to send email from Salesforce (not using apex). Modified component controller code..
I"m trying to eliminate possible causes of the failure, so I'd like to remove the sendInBulk method for now. For testing purposes, can you try moving you messaging.sendEmail out of the sendInBulk method and into the sendAndAttach method? Something like this:
-Greg