Skip to Content

How to send an email with attachments in SAP Cloud Application Studio

This blog is an extension of the amazing Melvin Hidalgo’s document:

Our objective is to send an email with a custom attachment (or pdf generated by the studio).

In order to do this, I created a custom BO with an element DUMMY and an action “SENDMAIL”.


In the implementing script for the action, I followed the Melvin’s instructions by creating an EmailActivity instance with a subject and then I added parties and the email body:



Finally our code in order to add some attachments. The first one is an empty attachment (just for test, but you can add a custom binary (base64) content. The second one is a pdf generated using a form binded to our custom BO containing the dummy element:





Unfortunately it seems that actually, there is no way to add a name for the sent attachments (if someone succeeds to do this please contact me).

You must be Logged on to comment or reply to a post.
  • Hey guys, there is also a very flexible key user based email/task notifications feature that is enabled for the Cloud for Customer BOs so you dont have to write code to do rules based notifications which can be sent to named CRM users as well as to customer records. You can define any number of rules/conditions, define the content which may be HTML based templates, and then the recipient of that email/task. You can find this feature is Administrator or Application User Management Work Center -> Workflow -> Notification Rules

    Screen Shot 2013-12-12 at 9.53.00 AM.png

    Screen Shot 2013-12-12 at 9.53.00 AM.png
  • Hi Alessandro,

    Will the above code to attach a file to email, as it is, help me in following requirement too :

    Requirement: In Opportunity View -> My New Tab -> My Advanced List Pane -> My column -> here I want to provide some control in such a way that I should be able to add an attachment to this column, and ultimately to the list pane and Opportunity.

    * In properties, the display type My Column is set to File Upload. Is this correct ?

    * Also if this should be File Upload, do we need to provide any event for 'OnFileSelect' ?

    * Also do we need to provide a seperate dialog box and script to add an attachment, that will be triggered on selecting a file ?

    The FileUpload button allows me to select the file, but as I save the list and opportunity instance, it gets saved without any actual file uploaded as attachments. I am basically stuck here and not finding proper solution.

    Any pointers or help will be appreciated.


  • Hi Alessandro,

    there is some changes in activity BO, SAP standard DEV do not recommend  to use this method to send mail, because it may become invalid in the future, maybe 1402 version.

    I also don't know other method to send mail, 🙂

    BTW: did you check the PDF file you send to other person, maybe you can't open it successfully, because of encode problem in activity BO.

    Kind Regards.


    • Hi Fred, I checked the PDF and is working.

      Thank you for the information, are you sure that this method will become unavailable? This is a very bad thing also because customers cannot decide to upgrade their tenant or not.

      For our project this is a requirement and cannot be fullfilled by in other ways

      • Unfortunately even we are about to use this method in our requirements. So we should find some way to avoid it if this is the case.

        But can customers choose not to upgrade ? I believe at least multiple customer would share the same system with different tenants and one customer cannot say I do not want to upgrade. Probably someone from SAP should comment on this ? 



        • Hi Vindod,

          Customers can definetly not choose to not upgrade their tenant. Fred is also right that there will be a change for the Acitivity BO. I will try to get a statement regarding that or some additional information. But it will be next year I guess.


          • Hi Jens,

            Thanks for the clarification and we would love to hear what SAP is working on or going to change in future so that we can align our developments accordingly. Thanks in advance.



          • Hi all,

            I got an answer.

            1. The coding against the Acitivity BO should still work in 1402. But it will be deprecated, so partners need to change the coding to work with the new BusinessActivity BO.

            2. Using Activity BO for e-mailing in general: Actually this BO is for tracking business activites between sales reps and customers and not to be used as general "mailing tool (Though it can be used as such). There is an OutputRequest BO which might be released for such purpose. I will try to get more details on that.

            Cheers and happy new year,


          • Hi Jens,

            In the second point of your explanation you meant to say, we can't create an emailActivity using ABSL logic even though we have feasibility of doing it in 1402??

            If yes, I did't find the BO, RequestOutPut in the 1402 cloud studio repository..!!

            Is there any other way to create an EmailActivity ?? I know the Notification rule but i wanted to create an EmailActivity using ABSL logic?



          • Hi Mani.

            See my sample code (its a little complicated but you can extract interesting lines):


                Add your SAP Business ByDesign scripting language implementation for:

                    Business Object: CustomerQuote

                    Node: Root

                    Event: BeforeSave



                  - To access the elements of the business object node,

                    use path expressions, for example, this.<element name>.

                  - To use code completion, press CTRL+J.


            import ABSL;

            import AP.PC.ActivityManagement.Global;

            import AP.CRM.Global;

            import AP.Common.GDT as GlobalDataTypes;

            import BASIS.Global;

            //for the pdf

            import DocumentServices.Global;

            //Email vars

            var elEmailRoot : elementsof Activity; 

            var elEmailParty: elementsof Activity.Party;

            var instEmail; 

            var emailBody; 

            var emailSubject:LANGUAGEINDEPENDENT_LONG_Text; 

            var TXT_TYPE_BODY_TEXT = "10002";

            var elEmailTxtColl: elementsof Activity.TextCollection;

            var elEmailTxtCollTxt: elementsof Activity.TextCollection.Text; 

            var elEmailTxtCollTxtCntnt: elementsof Activity.TextCollection.Text.TextContent; 

            var instEmailTxtColl; 

            var instEmailTxtCollTxt; 

            var instEmailTxtCollTxtCntnt;

            var instEmailAttFld;

            var doctype : GlobalDataTypes:DocumentTypeCode;

            var binaryObject : BinaryObject;

            var docDesc : GlobalDataTypes:Description;

            var docName : GlobalDataTypes:LANGUAGEINDEPENDENT_Name;

            var docAltName : GlobalDataTypes:LANGUAGEINDEPENDENT_Name;

            var bin : BinaryObject.content;

            //Other vars

            var qryQuote_QueryByElements;

            var selParamsQuote_QueryByElements;

            var qryQuote_Result;

            var qryInAppParty_QueryByElements;

            var selParamsInAppParty_QueryByElements;

            var qryInAppParty_Result;

            var qryApprovedParty_QueryByElements;

            var selParamsApprovedParty_QueryByElements;

            var qryApprovedParty_Result;

            var qryRejectedParty_QueryByElements;

            var selParamsRejectedParty_QueryByElements;

            var qryRejectedParty_Result;

            var QuoteReplicatedParty : QuoteParty;

            var ReplicatedParty : QuoteParty.ReplicatedParties;

            var Quote : CustomerQuote;

            var Party : CustomerQuote.Party;

            var PartyColl : collectionof(CustomerQuote.Party);

            var PartyCollCC : collectionof(CustomerQuote.Party);

            var QuoteApprovalStatus = this.Status.ApprovalStatusCode;

            var InAppEmailSent = this.approval_required_mail_sent;

            var ApprovedEmailSent = this.approved_mail_sent;

            var RejEmailSent = this.rejected_mail_sent;

            //Email body vars

            var id = this.ID.content.RemoveLeadingZeros();

            var account;

            if (this.BuyerParty.IsSet() && this.BuyerParty.Party.IsSet()){

                account = this.BuyerParty.Party.CurrentName.PartyFormattedName.content;


            var lv_title = "<div style=\"font-weight:bold;\">Please find here enclosed the Quotation no. "+id+"</div><br /><br />";

            var lv_start = "<div style=\"background-color:#eeeeee;\">";

            var lv_status = "<div><span style=\"font-weight:bold;\">Status: </span>"+this.Status.ApprovalStatusCode.GetDescription()+"</div><br />";

            var lv_middle = "<br /><br />FOR YOUR INFORMATION<br /><br /><br />";

            var lv_customer;

            if (this.BuyerParty.IsSet()){

                lv_customer =  "<div><span style=\"font-weight:bold;\">Customer number: </span>"+this.BuyerParty.PartyKey.PartyID.content.RemoveLeadingZeros()+"</div><br />";


            var lv_customer_n =  "<div><span style=\"font-weight:bold;\">Customer name: </span>"+account+"</div><br />";

            var lv_resp;

            if (this.EmployeeResponsibleParty.IsSet() && this.EmployeeResponsibleParty.Party.IsSet()){

                lv_resp =  "<br/><div><span style=\"font-weight:bold;\">Account manager: </span>"+this.EmployeeResponsibleParty.Party.CurrentName.PartyFormattedName.content+"</div><br />";


            var lv_segm =  "<br/><div><span style=\"font-weight:bold;\">Segment: </span>"+this.Quot_Division.GetDescription()+"</div><br />";

            var lv_subseg =  "<div><span style=\"font-weight:bold;\">SubSegment: </span>"+this.Quot_SubSegment.GetDescription()+"</div><br />";

            var lv_appfld =  "<div><span style=\"font-weight:bold;\">Application field: </span>"+this.Quot_AppField.GetDescription()+"</div><br />";

            var lv_payterm;

            if (this.CashDiscountTerms.IsSet()){

                lv_payterm =  "<br/><div><span style=\"font-weight:bold;\">Payment terms: </span>"+this.CashDiscountTerms.Code.GetDescription()+"</div><br />";


            var lv_incterm;

            if (this.DeliveryTerms.IsSet() && (!this.DeliveryTerms.Incoterms.IsInitial())){

                lv_incterm =  "<br/><div><span style=\"font-weight:bold;\">Incoterms: </span>"+this.DeliveryTerms.Incoterms.ClassificationCode.GetDescription()+" "+this.DeliveryTerms.Incoterms.TransferLocationName+"</div><br />";


            var lv_endvald =  "<br/><div><span style=\"font-weight:bold;\">End validity date: </span>"+this.ValidityPeriod.TimePointPeriod.EndTimePoint.Date.ToString()+"</div><br />";

            var lv_end = "</div><br />";

            //Code start!

            QuoteReplicatedParty = QuoteParty.Retrieve(this.ID);

            //Get old quote data

            qryQuote_QueryByElements = CustomerQuote.QueryByElements;

            selParamsQuote_QueryByElements = qryQuote_QueryByElements.CreateSelectionParams();

            selParamsQuote_QueryByElements.Add(qryQuote_QueryByElements.ID.content, "I","EQ", this.ID.content);

            qryQuote_Result = qryQuote_QueryByElements.Execute(selParamsQuote_QueryByElements);

            var cont = qryQuote_Result.Count();

            if (cont == 0) { //creation fase



            //TO DECOMMENT

            //Get all "in approval" party rules for notification

            qryInAppParty_QueryByElements = QUOT_IN_APP_PARTIES.QueryByElements;

            //selParamsInAppParty_QueryByElements = qryInAppParty_QueryByElements.CreateSelectionParams();

            //qryInAppParty_Result = qryInAppParty_QueryByElements.Execute(selParamsInAppParty_QueryByElements);

            qryInAppParty_Result = qryInAppParty_QueryByElements.Execute();

            //Get all "approved" party rules for notification

            qryApprovedParty_QueryByElements = QUOT_APPROV_PARTIES.QueryByElements;

            //selParamsApprovedParty_QueryByElements = qryApprovedParty_QueryByElements.CreateSelectionParams();

            //qryApprovedParty_Result = qryApprovedParty_QueryByElements.Execute(selParamsApprovedParty_QueryByElements);

            qryApprovedParty_Result = qryApprovedParty_QueryByElements.Execute();

            //Get all "rejected" party rules for notification

            qryRejectedParty_QueryByElements = QUOT_REJEC_PARTIES.QueryByElements;

            //selParamsRejectedParty_QueryByElements = qryRejectedParty_QueryByElements.CreateSelectionParams();

            //qryRejectedParty_Result = qryRejectedParty_QueryByElements.Execute(selParamsRejectedParty_QueryByElements);

            qryRejectedParty_Result = qryRejectedParty_QueryByElements.Execute();



            foreach (Quote in qryQuote_Result){

                //Trace.Info(TaskSubStatus, "substatus 1");

                //Trace.Info(Task.Task_SubStatus.content, "substatus 2");


                if ((QuoteApprovalStatus != Quote.Status.ApprovalStatusCode) ) { //The email starts if the approval status field changed

                    //In Approval, Approved, Rejected check

                    if ((QuoteApprovalStatus != "3") && (QuoteApprovalStatus != "4") && (QuoteApprovalStatus != "6")){




                    //This check to avoid duplicates

                    if ((QuoteApprovalStatus == "3") && (InAppEmailSent == true)){



                        if (QuoteApprovalStatus == "3"){

                            this.approval_required_mail_sent = true;


                            this.approval_required_mail_sent = false;



                    if ((QuoteApprovalStatus == "4") && (ApprovedEmailSent == true)){



                        if (QuoteApprovalStatus == "4"){

                            this.approved_mail_sent = true;


                            this.approved_mail_sent = false;



                    if ((QuoteApprovalStatus == "6") && (RejEmailSent == true)){



                        if (QuoteApprovalStatus == "6"){

                            this.rejected_mail_sent = true;


                            this.rejected_mail_sent = false;



                    //Logic to select the party accordingly to the Status

                    foreach (Party in this.Party){

                    //select the party

                        foreach (ReplicatedParty in QuoteReplicatedParty.ReplicatedParties){

                        //if the party flag has been selected by the user add the party to the collection

                            if ((ReplicatedParty.partyUUID.content == Party.PartyUUID.content) && (ReplicatedParty.partyRole == Party.RoleCode) && ReplicatedParty.partyFlag == true){

                                if (QuoteApprovalStatus == "3"){

                                    foreach (var status_rule in qryInAppParty_Result.Where( n => n.PARTYFUNC.content.Trim() == Party.RoleCode.Trim() )){

                                        if (status_rule.TO == true){



                                        if (status_rule.CC == true ){





                                if (QuoteApprovalStatus == "4"){

                                    foreach (var status_rule in qryApprovedParty_Result.Where( n => n.PARTYFUNC.content.Trim() == Party.RoleCode.Trim() )){

                                        if (status_rule.TO == true){



                                        if (status_rule.CC == true ){





                                if (QuoteApprovalStatus == "6"){

                                    foreach (var status_rule in qryRejectedParty_Result.Where( n => n.PARTYFUNC.content.Trim() == Party.RoleCode.Trim() )){

                                        if (status_rule.TO == true){



                                        if (status_rule.CC == true ){








                    //Email instance creation

                    emailSubject = "Quotation n° " + id + " " + account; 

                    elEmailRoot.SubjectName = emailSubject; 

                    //CODE 1402

                    elEmailRoot.TypeCode = "39";

                    instEmail = Activity.Create(elEmailRoot); 

                    //TO DECOMMENT

                    //Add parties

                    if (PartyColl.Count() == 0 && PartyCollCC.Count() == 0){



                    foreach (Party in PartyColl){

                        //1402 elEmailParty.PartyKey.PartyID.content = Party.PartyKey.PartyID.content; 

                        elEmailParty.PartyName = Party.PartyKey.PartyID.content; 



                    foreach (Party in PartyCollCC){

                        //1402 elEmailParty.PartyKey.PartyID.content = Party.PartyKey.PartyID.content;     

                        elEmailParty.PartyName = Party.PartyKey.PartyID.content; 




                    //TO REMOVE - Add parties

                    //elEmailParty.PartyKey.PartyID.content = "8000000102"; 


                    //Create body

                    //instEmailTxtColl = instEmail.TextCollection.Create();

                    //elEmailTxtCollTxt.TypeCode.content = TXT_TYPE_BODY_TEXT;

                    //instEmailTxtCollTxt = instEmailTxtColl.Text.Create(elEmailTxtCollTxt);

                    //elEmailTxtCollTxtCntnt.Text.content = "<html><head></head><body>test<body></html>"; //                           Here the message itself


                    //Create att folder

                    instEmailAttFld = instEmail.AttachmentFolder.Create();

                    //Add html attachment as body (TODO)

                    docName = "body.html";

                    doctype.content = "10001";

                    binaryObject.mimeCode = "text/html";

                    binaryObject.content = Binary.ParseFromString("<html><head></head><body>"+lv_title+lv_start+lv_status+lv_middle+lv_customer+lv_customer_n+lv_resp+lv_segm+lv_subseg+lv_appfld+lv_payterm+lv_incterm+lv_endvald+lv_end+"<body></html>"); //bin;

                    //binaryObject.content = Binary.ParseFromString("<html><head></head><body>test<body></html>");

                    instEmailAttFld.CreateFile(doctype, docName, docAltName, docDesc, binaryObject);



                    //PDF Generation

                    //var FormTemplateLanguage:LANGUAGEINDEPENDENT_MEDIUM_Text = "E";

                    var PDF : BinaryObject;

                    //var FormTemplateCode : OutputRequestFormTemplateCode;

                    //FormTemplateCode.content = "C0002"; //Code is Form Template Header Code

                    //PDF = OutputManagementUtilities.GetPDF(this,FormTemplateCode,FormTemplateLanguage);

                    docName = "QuotationSnapshot.pdf";

                    doctype.content = "10001";

                    //binaryObject = PDF; //Binary.ParseFromString(Binary.ToBase64String("iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==")); //Binary.ParseFromString("111111111");

                    //instEmailAttFld.CreateFile(doctype, docName, docAltName, docDesc, binaryObject);

                    foreach (var pdf in this.AttachmentFolder.DocumentList){

                        if (pdf.MIMECode == "application/pdf"){

                            binaryObject = pdf.FileContent.BinaryObject;

                            instEmailAttFld.CreateFile(doctype, docName, docAltName, docDesc, binaryObject);












  • Hi Alessandro,

    I am implementing email template with business extension for travel and expense report.

    I am using these lines of code to send approval mail,

    EMailUtilities.SendEMail(lang, bcc, cc, toAddress, htmlContent, from name, subject);

    in absl file.

    I am not able to use your method to send emails, namespaces are not available,

    and we have requirement that need to attach files with an email,

    Please, can you give any suggestions to attach files in the SendEMail() method,

    Thanks & Regards,

    Preethi Ande

  • Dear Experts,

    I have added custom button on my screen and have assigned this method. I have clicked on button send email but how we can check email is send successfully or not.

    Can you please help me ?


    Puneet Mittal

      • Hi Alessandro Iannacci

        Can you please guide me how to send custom PDF form in email attachment? I am able to send an email with a pdf attachment but i am unable to open it as it is giving some message as " was set as an email attachment and wasn't correctly decoded"



          • Hi Alessandro Iannacci

            My requirement is to add a button on Tickets WC view in WC service. Button is Send Email. On click of that button an email should be triggered with an attachment of custom pdf form.

            1. I have made a custom pdf form and created a EC and a button preview on it and On click of that button I am able to see my form.

            2. But when I tried the same thing with Send Email button where I have created an action Send_Email which populates the Email Body with HTML text and in attachments I am passing the group code of my form I am not able to open the same. It does not gets opened up and gives some decoding issue

            I am attaching screenshots for action send_email. Am I missing something?


          • Please try with a simple form with just one label, to understand if it is a problem with the email framework or with the generation of the pdf.


  • Hi All,

    Since the EmailActivity is now deprecated, I am trying to make use of Activity BO to perform send mail, but could not achieve the same. i have these points unanswered:

    1. Without creating instance in Activity can I send the mail when Outlook is not configured?
    2. In Activity BO, Party.EmailURI is read only what can be done to specify the sender?

    Please guide.




  • Thanks for this valuable blog.

    I have one requirement.  I need to send a mail if Sales Order is released.

    Mail Content:

    Body: <content>

    Attachment: If I click preview button in Sales Order screen, the same exact pdf need to send as mail.

    What is the group code for the Standard form? (Ex. Sales Order)

    Regarding my question, I have raised a separate thread. Please help me to solve this issue. 


    Sankaran A