4

A little background. We created a new object for our product called 'Systems'. This 'Systems" object has a multi picklist field for 'Add-Ons' depending on what the customer has loaded on their system. Each 'System' is tied to an account and an account can have multiple systems.

My end goal of this project is to have a salesperson use a template to auto-generate an email to a contact. This email template will look through all the systems for a particular account, figure out which add-ons they don't have, and populate the email with verbiage for those add-ons. I don't know if this is possible, but it would be great to use this email template in Gmail (using Cirrus Insight) and have an entry field pop up where the user can enter the account name. Cirrus allows users to enter specific info into merge fields for things like {!Contact} or {!Opportunity} but I don't know how this would work for a "variable" being passed into a customer controller. It is not a merge field so I don't think Cirrus can handle this. Is there a way to create a pop-up window?

I quickly found out that creating this email template was going to be more complicated than just using Salesforce Merge fields. I need the ability to add or remove entire paragraphs depending on an account's data. I did some research and found that this could possibly be done using Visualforce Templates, Components, and Apex Controllers.

I was able to use a controller, component, and template to pull add-on data from an account using the API call with an account name hardcoded in. I need the ability for a user to enter in which account they are emailing about, however.

I threw together some code that doesn't look quite right but I feel I am stuck. I am starting small with basics and only trying to display all the add-ons a particular account has in the email for now. Can anyone help me out here? Is there a way easier way to do this that I am missing?

VisualForce Template:

<messaging:emailTemplate subject="Test to Pass Value" recipientType="Contact" relatedToType="Account">
<messaging:HTMLEmailBody >
    account: {!relatedTo.Name}
    <c:EmailComp ToID="{!relatedTo.Id}"/> 
</messaging:HTMLEmailBody>

VisualForce Component:

<apex:component controller="EmailController" access="global">
<apex:attribute name="ToID" type="ID" description="the account ID" assignTo="{!accountID}"/>
<h1>{!account.Name}</h1>
<apex:dataTable value="{!AddOns}" var="addOns">
    <apex:column headerValue="Add On">
        {!addOns}
    </apex:column>
</apex:dataTable>

Apex Controller:

public class EmailController {

    public ID accountID{set; get;}

    public Account account {
        get {

            if (accountId != null && account == null) {
                account = [
                    SELECT Name
                    FROM Account
                    WHERE Id = :accountID
                ];
            }

            // Always return the account
            return account;
        }

        set;
    }


    public List<System__c> addOns {
        get {
            if (this.account != null 
                //&& this.account.Add_Ons__c != null
                && this.addOns == null) {
                this.addOns = new List<System__c>();

                String accountString = '' + this.account;

                List<System__c> allAddOns = Database.query('Select Add_ons__c From system__c Where account__r.Name = ' + accountString);

                addOns = allAddOns;    

            }

            return addOns;
        }

        set;
    }
}
2
  • 3
    This is a good first post. Can you edit your post to explain how the user will trigger the email process? Do you expect them to click the Send Email button in the Activity History section of the Account record, or do some other process that will trigger the sending of the email? Commented Jan 4, 2018 at 20:20
  • @DavidCheng Thank you for your response. I have updated the second paragraph to better describe what I am looking for. I am sorry if it is vague (or completely impossible!), still pretty new to Salesforce. I feel like I am taking a very complex route for what seems like a simple task.
    – Rory
    Commented Jan 5, 2018 at 19:58

1 Answer 1

2

The biggest challenge you're going to have is triggering your emails. Your requirement of needing to be able to manually select the account (as opposed to sending to the account associated with the contact) is going to be hard to fulfill. You may need a custom VF page.

I've taken your code and turned it into something that should work to accomplish the goal of displaying all of a specified account's add-ons.

I prefer using the getters/setters using the following syntax. You can accomplish the same thing by moving the code into more explicit methods if you want and get the same result. (added another sample at bottom showing explicit getters).

You create the attribute but never assign a value to it:

<messaging:emailTemplate subject="Test to Pass Value" recipientType="Contact" relatedToType="Account">
    <messaging:HTMLEmailBody >
        account: {!relatedTo.Name}
        <c:EmailComp ToID="{!relatedTo.Id}"/> <!-- Pass account Id to component -->
    </messaging:HTMLEmailBody>
</messaging:emailTemplate>

You're controller has... too much. For something as simple as just returning the addons, the following should do just fine.

public class EmailController {
    public ID accountID;

    public Account account {
        get {
            // If it's null, populate it
            // This ensures the query only runs once
            if (accountId != null && account == null) {
                account = [
                    SELECT Name,
                        ( // Sub-query for the child objects
                            SELECT Add_Ons__c
                            FROM Systems__r // <-- the child relationship name of the object
                        )
                    FROM Account
                    WHERE Id = :accountID
                ];
            }

            // Always return the account
            return account;
        }

        set;
    }

    public List<String> addOns {
        get {
            if (this.account != null 
                && this.account.Systems__r != null
                && this.addOns == null) {
                this.addOns = new List<String>();

                for (System__c s : this.account.Systems__r) { // Loop through child objects
                    if (s.Add_Ons__c != null) {
                        for (String a : s.Add_Ons__c.split(';')) { // Get add ons from each object
                            this.addOns.add(a);
                        }
                    }
                }
            }

            return addOns;
        }

        set;
    }
}

Your component code to display the addons

<apex:component controller="EmailController" access="global">
    <apex:attribute name="ToID" type="ID" description="the account ID" assignTo="{!accountID}"/>
    <h1>{!account.Name}</h1>
    <apex:dataTable value="{!addONs}" var="s">
        <!-- The header facet is for more advance uses. To just display
             text, use the headerValue attribute instead. -->
        <apex:column headerValue="Add On">
            {!s}
        </apex:column>
    </apex:dataTable>
</apex:component> 

Using more explicit getters:

public class EmailController {
    public Account account;
    public ID accountID;
    public List<String> addOns;

    public account getAccount() {
        if (this.accountId != null && this.account == null) {
            this.account = [
                SELECT Name,
                    (
                        SELECT Add_Ons__c
                        FROM Systems__r
                    )
                FROM Account
                WHERE Id = :accountId
            ];
        }

        return account;
    }

    public List<String> getAddOns() {
        if (this.account != null
            && this.account.Systems__r != null
            && addOns == null) {
            addOns = new List<String>();

            for (System__c s : this.account.Systems__r) {
                if (s.Add_Ons__c != null) {
                    for (String a : s.Add_Ons__c.split(';')) {
                        addOns.add(a);
                    }
                }
            }
        }

        return addOns;
    }
}
9
  • This seems to be the path I want to go down. It is not letting me create a list of type 'Add_ons__c' though. I think this is because 'Add_ons__c' is a multi picklist field of 'System__c' and 'System__c' objects are added under an 'Account' with by a lookup relationship. Should I just make the return a String instead of a list? I can make the query at the end look like: [ SELECT Add_ons__c FROM System__c WHERE Account__r.ID = :accountId ]
    – Rory
    Commented Jan 5, 2018 at 22:57
  • Updated my answer. I'd like to add that the vast majority of developers/admins I have met have grown to hate mutli-select pick-lists. They are a nightmare to work with, terrible for reporting and cause issues when trying to integrate them with systems out side ofSalesforce. I'd recommend switching to checkboxes (if you have only a few add-ons) or a custom object if you have many.
    – gNerb
    Commented Jan 8, 2018 at 15:37
  • Thanks for getting back to me. I have made a few changes to my code with no luck still. I have edited the main post with updated code. For some reason, when I query for 'Add_ons__c' it returns the "System__c' object. I feel like I am getting close. The email template is getting an error when I test it out which is actually making me optimistic.
    – Rory
    Commented Jan 8, 2018 at 22:21
  • I'm sorry, I completely misunderstood your previous comment about how the add-ons are related. Ill try to update the answer again.
    – gNerb
    Commented Jan 9, 2018 at 16:08
  • @John Updated my answer to get it more inline with how you said the relationships work. Hopefully that gets you the last little bit.
    – gNerb
    Commented Jan 9, 2018 at 16:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .