function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK 

Pdf upload failed apex

Hi

I am trying to send an email with pdf whenever a pdf is attached in "Notes and Attachment" section. Whenever I try to upload it is giving me an error 

10:04:27:005 FATAL_ERROR System.EmailException: SendEmail failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, No body supplied for the file attachment.: [fileAttachments]

Please find the below code for Apex trigger.
trigger EmailTrigger on ContentVersion (after insert) 
{
List<Messaging.SingleEmailMessage> allMessages = new List<Messaging.SingleEmailMessage>();
for(ContentVersion cv : trigger.new)
{
Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
attachment.setBody(cv.ContentBodyId);
attachment.setFileName(cv.Title + '.' + cv.FileType);

Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.toAddresses = new String[] { 'andre.nobre@cleantechsolar.com' };
message.subject = 'Test Mail';
message.plainTextBody = 'Attached file name: ' + cv.Title;
message.setFileAttachments(new Messaging.EmailFileAttachment[] {attachment});

allMessages.add(message); 
}

Messaging.sendEmail(allMessages);
}
Kindly help me to rectify the same.
 
jigarshahjigarshah
As per the Messaging.EmailFileAttachment documentation (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_attachment.htm#apex_Messaging_EmailFileAttachment_constructors), the body parameter requires a value of type Blob. Hence update line # 7 within your trigger code as below. Hope this helps.
attachment.setBody(Blob.valueof(cv.ContentBodyId));
Moreover, you should also add code blocks to check that emails are sent only an After Insert or else, emails would be sent on every insert operation which will result in you running out of your 24 hour email limit which is a small number. Update the trigger code as follows
trigger EmailTrigger on ContentVersion (after insert){

	if(Trigger.isAfter){
		
		if(Trigger.isInsert){
		
			//Add your code to send emails with attachments here
		}
	}
}
Please mak the thread as SOLVED and answer as the BEST ANSWER if it helps resolve your issue.
Salesforce DeveloperSalesforce Developer
Tried something as below and it is working perfectly fine:
 
trigger EmailTrigger on Attachment (after insert) 
{
   List<Messaging.SingleEmailMessage> listMails = new List<Messaging.SingleEmailMessage>();
    for(Attachment at: Trigger.New){
        Messaging.EmailFileAttachment att = new Messaging.EmailFileAttachment();
        att.filename = at.Name;
        att.contenttype = at.ContentType;
        att.setBody(at.Body);
        Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
        string s = 'Attachment added';
        msg.PlainTextBody = s;
        msg.subject = 'Attachment added:'+ at.Name;
        
        List<string> toaddr = New String[] {'test@test.com'};
        msg.toaddresses =  toaddr;
        
        msg.setFileAttachments(new messaging.EmailFileAttachment[]{att});
        listMails.add(msg);
        Messaging.sendEmail(listMails);
    }
}

 
jigarshahjigarshah
KRISHNAMURTHY, if this issue has been addressed for you, please mark the thread as SOLVED and answer as the BEST ANSWER that helped you most in addressing your issue.
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK
Hi Jigarshah,

Your code helped me to activate the trigger, but I would like to know how to tag fields from Invoice object ?

Invoice object is a custom object and the "Notes and Attachment" related list is present in invoice object.

Thanks for your immense help!
jigarshahjigarshah
Are you trin to include field values from the Invoice object within the attachment?
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK
Yes absolutely right
jigarshahjigarshah
Refer the code provided in this article on Rendering a Visualforce Page as a PDF (https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_output_pdf_render_in_apex.htm) wihtin the Visualforce Developer Guide to render the Invoice Page as a PDF and then send it as an email attachment.

The technique is as follows.
  1. Create a new Visualforce page that displays the Invoice information and is formatted. This page would be ultimately rendered as a PDF. Lets call it the InvoicePdfAttachment.
  2. Write a second page which has the Apex Controller to generate the InvoicePdfAttachment as a pdf and attaches it to the email body and sends out an email. You will need to use the getContentAsPDF()  of the Pagereference class to generate the Pdf.
Please mak the thread as SOLVED and answer as the BEST ANSWER if it helps resolve your issue.
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK
Hi Jigarshah,

I don't wanna create a new page. I just wanna tag invoice object fields in subject and body. What should I be doing here and how do I write a test class for the below code.
 
trigger EmailTrigger on ContentVersion (after insert){
    
    if(Trigger.isAfter){
        
        List<Messaging.SingleEmailMessage> allMessages = new List<Messaging.SingleEmailMessage>(); 
        if(Trigger.isInsert){
            
            
            for(ContentVersion cv : trigger.new){ 
                Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment(); 
                attachment.setBody(Blob.valueof(cv.ContentBodyId));
                attachment.setFileName(cv.Title + '.' + cv.FileType); 
                
                Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); 
                message.toAddresses = new String[] { 'andre.nobre@cleantechsolar.com' }; 
                message.ccAddresses = new String[] {'krishnamurthy.ka@cleantechsolar.com'};
                message.subject = 'Test Mail'; 
                message.plainTextBody = 'Attached file name: ' + cv.Title; 
                message.setFileAttachments(new Messaging.EmailFileAttachment[] {attachment}); 
                
                allMessages.add(message); 
            } 
        }
        Messaging.sendEmail(allMessages); 
    }
}

 
jigarshahjigarshah
You will need to use a SOQL Query to fetch and hold the records in an instance of type Invoice__c assuming it to be a custom object. Sample code to query the Invoice__c (assuming Invoice__c is the API Name for Invoice object)is as below. Once you get hold of the fields, create a desired string with the Invoice values and add it to the Subject and PlainTextBodyBody.
 
trigger EmailTrigger on ContentVersion (after insert){
    
    if(Trigger.isAfter){
        
        List<Messaging.SingleEmailMessage> allMessages = new List<Messaging.SingleEmailMessage>(); 
        if(Trigger.isInsert){
        
		    //Retrieve all invoices based on the requried Invoice Ids. Add your criteria to fetch 
		    // relevant invoices in the Where clause based on the Invoice and ContentVersion 
		    // object relationship. Replace <yourInvoiceIdSet> below with the actual Id Set
            List<Invoice__c> invoiceList = [Select Id, Name, Invoice_Number__c 
										    From Invoice__c 
										    Where Id IN :<yourInvoiceIdSet>];
		
            for(ContentVersion cv : trigger.new){

                Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment(); 
                attachment.setBody(Blob.valueof(cv.ContentBodyId));
                attachment.setFileName(cv.Title + '.' + cv.FileType); 
                
                Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); 
                
				message.toAddresses = new String[] {'andre.nobre@cleantechsolar.com' }; 
                message.ccAddresses = new String[] {'krishnamurthy.ka@cleantechsolar.com'};
                
				//Subject with the retrieved Invoice Number
				message.subject = 
					String.format('Your Invoice having Invoice # {0} is due for payment', 
						new String[]{invoiceList[0].Invoice_Number__c}); 
                
				//Email body containing Name with the retrieved invoice
				message.plainTextBody = 
					String.format('Your invoice for {0} with the file name {1} is attached alongwith.', 
						new String[]{invoiceList[0].Name, cv.Title});
						
                message.setFileAttachments(new Messaging.EmailFileAttachment[] {attachment}); 
                allMessages.add(message); 
            } 
        }
        Messaging.sendEmail(allMessages); 
    }
}

Once your code works as desired you can then implement the test code by inserting the relevant records i.e. Invoice & associated Content which will automatically invoke the code written on After Trigger. Only thing is you may, need to use Test.isRunningTest() in the code above to implement the branching logic for avoiding email sending and consumption of your email limits when the email sending code is invoked through Test class execution.
 
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK
Hi Jigarshah,

Thanks for your wonderful explanation. Appreciate your time on this. I have written a test code which looks like this. Kindly assist me if it's correct and let me know what changes need to be done so that it is done correctly.
 
@isTest 
public class EmailTriggerTest 
{
    static testMethod void testMethod1() 
	{

		ContentVersion testContentInsert =newContentVersion(); 
		testContentInsert.ContentURL='<a target="_blank" href="http://www.google.com/" rel="nofollow">http://www.google.com/</a>'; 
		testContentInsert.Title ='Google.com'; 
		insert testContentInsert; 
		
		ContentVersion testContent = [SELECT ContentDocumentId FROM ContentVersion where Id = :testContentInsert.Id]; 
		
 
    }
}

 
jigarshahjigarshah
Please share the copy of your final working Apex trigger code for which you written your test code. Also check if the current written test code gets you the appropriate test code coverage. Ideally your test code should cover every line of code within your business logic. Regards, Jigar Shah
KRISHNAMURTHY KOLUMAM ANANTHARAMAKKRISHNAMURTHY KOLUMAM ANANTHARAMAK
Hi Jigarshah,

It works super fine. See the below code. I am stuck with test class to deploy in production.
 
trigger EmailTrigger on ContentVersion (after insert){
    
    if(Trigger.isAfter){
        
        List<Messaging.SingleEmailMessage> allMessages = new List<Messaging.SingleEmailMessage>(); 
        if(Trigger.isInsert){
        
		    //Retrieve all invoices based on the requried Invoice Ids. Add your criteria to fetch 
		    // relevant invoices in the Where clause based on the Invoice and ContentVersion 
		    // object relationship. Replace <yourInvoiceIdSet> below with the actual Id Set
            List<Invoice_Payment__c> invoiceList = [Select Id, Name
										    From Invoice_Payment__c 
										   where Id IN (SELECT FirstPublishLocationId FROM ContentVersion WHERE Id IN :trigger.new)];
		
            for(ContentVersion cv : trigger.new){

                Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment(); 
                attachment.setBody(Blob.valueof(cv.ContentBodyId));
                attachment.setFileName(cv.Title + '.' + cv.FileType); 
                
                Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); 
                
					message.setToAddresses(new String[] { 'andre.nobre@cleantechsolar.com' ,'rupesh.baker@cleantechsolar.com'});
                    message.setccAddresses(new String[] { 'krishnamurthy.ka@cleantechsolar.com' ,'divina.minoza@cleantechsolar.com'});
                
				//Subject with the retrieved Invoice Number
				message.subject = 
					String.format('Your Invoice having Invoice # {0} is due for payment', 
						new String[]{invoiceList[0].Name}); 
                
				//Email body containing Name with the retrieved invoice
				message.plainTextBody = 
					String.format('Your invoice for {0} with the file name {1} is attached alongwith.', 
						new String[]{invoiceList[0].Name, cv.Title});
						
                message.setFileAttachments(new Messaging.EmailFileAttachment[] {attachment}); 
                allMessages.add(message); 
            } 
        }
        Messaging.sendEmail(allMessages); 
    }
}