Via de Salesforce Flow actie: Send Mail, kun je maar naar 5 contactpersonen tegelijkertijd mailen. Dit is vaak veel te weinig. Hier zijn een aantal oplossingen voor. Zo zou je de “Send mail” actie in een loop kunnen zetten, maar dit is niet best-practice. Wil je het op een goede manier oplossen, dan zul je APEX moeten raadplegen. In deze tutorial gaan we starten met een flow en daarna de Mass E-mail verzenden met APEX. Uiteraard wordt ook alles gelogd bij de activity!
Use Case
In mijn geval wil ik een herinneringsmail sturen naar iedereen die een evenement bij wil wonen. Om dit voor elkaar te krijgen heb ik eerst een Record Triggered Flow gemaakt op het desbetreffende object en vervolgens iedereen die zich nog niet geannuleerd heeft in een Collection Variabele gezet. Verder heb ik het juiste template Id opgehaald en deze 2 waarden worden vervolgens naar APEX gestuurd om de mail te sturen.
APEX Script voor Mass E-mail
Eigenlijk moeten we beginnen met het APEX-script, aangezien je in de Salesforce Flow naar de APEX moet verwijzen, maar voor deze tutorial is het handig om te zien welke 2 waarden er naar het APEX script gestuurd worden. Hieronder toon ik het volledige Salesforce Mass E-mail script en we lopen het daarna stap voor stap langs:
public class BatchEmailReminderSender implements Database.Batchable {
public List bezoekers;
public Id emailTemplateId;
public BatchEmailReminderSender(List bezoekers, Id templateId) {
this.bezoekers = bezoekers;
this.emailTemplateId = templateId;
}
public Database.QueryLocator start(Database.BatchableContext bc) {
// Query om de gerelateerde contactrecords op te halen, inclusief het Name veld
return Database.getQueryLocator([
SELECT Id, E_mail__c, Contact__c, Contact__r.Email, Name
FROM Evenement_bezoeker__c
WHERE Id IN :bezoekers
]);
}
public void execute(Database.BatchableContext bc, List scope) {
List emails = new List();
for (Evenement_bezoeker__c bezoeker : (List) scope) {
if (bezoeker.Contact__c != null && bezoeker.Contact__r.Email != null) {
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setToAddresses(new String[] { bezoeker.Contact__r.Email });
email.setTemplateId(emailTemplateId);
email.setTargetObjectId(bezoeker.Contact__c);
email.setWhatId(bezoeker.Id); // Stel de WhatId in op het Evenement_bezoeker__c record
email.setSaveAsActivity(true); // Zorg ervoor dat de e-mail zelf wordt gelogd als een activiteit
emails.add(email);
}
}
Messaging.sendEmail(emails);
}
public void finish(Database.BatchableContext bc) {
// Eventuele opruimtaken
}
public class EmailBatchRequest {
@InvocableVariable
public List bezoekers;
@InvocableVariable
public Id emailTemplateId;
}
@InvocableMethod
public static void startBatch(List requests) {
for (EmailBatchRequest req : requests) {
BatchEmailReminderSender batch = new BatchEmailReminderSender(req.bezoekers, req.emailTemplateId);
Database.executeBatch(batch, 200);
}
}
}
APEX Script maken met VS Code
Om op een makkelijke manier een APEX script te starten, kun je in Visual Studio Code in de terminal het volgende commando gebruiken:
sf apex generate class –name BatchEmailReminderSender –output-dir force-app/main/default/classes
Er wordt dan gelijk 2 bestanden aangemaakt:
-BatchEmailReminderSender.cls
-BatchEmailReminderSender.cls-meta.xml
De .xml houden we gewoon standaard en zullen ons nu focussen op de .cls. We beginnen bovenaan:
public class BatchEmailReminderSender implements Database.Batchable {
public List bezoekers;
public Id emailTemplateId;
public BatchEmailReminderSender(List bezoekers, Id templateId) {
this.bezoekers = bezoekers;
this.emailTemplateId = templateId;
}
Door de interface tabase.Batchable
te implementeren, maakt de klasse gebruik van het batch framework van Salesforce. Dit framework vereist dat de klasse drie methoden implementeert: start
, execute
en finish
. Deze methoden komen later terug in de code. Vervolgens worden er twee klassenvariabelen gedefinieerd: een lijst van evenementbezoekers en een e-mailsjabloon ID. De constructor initialiseert de BatchEmailReminderSender
klasse met de gegeven lijst van bezoekers en het e-mailsjabloon ID. Wanneer een object van deze klasse wordt gemaakt, worden de bezoekers
en emailTemplateId
ingesteld met de waarden die aan de constructor worden doorgegeven. Deze waarden zijn afkomstig van de Salesforce Flow.
public Database.QueryLocator start(Database.BatchableContext bc) {
// Query om de gerelateerde contactrecords op te halen, inclusief het Name veld
return Database.getQueryLocator([
SELECT Id, E_mail__c, Contact__c, Contact__r.Email, Name
FROM Evenement_bezoeker__c
WHERE Id IN :bezoekers
]);
}
Batch Framework: Start
De start
methode is een verplicht onderdeel van een klasse die de Database.Batchable<SObject>
interface implementeert. Deze methode initialiseert het batchproces en specificeert welke records moeten worden verwerkt.
Database.QueryLocator: Dit is het type dat door de methode wordt geretourneerd. Een QueryLocator
is een object dat een grote set van records in Salesforce vertegenwoordigt.
Het object Database.BatchableContext bc: is een contextobject dat door Salesforce wordt doorgegeven aan de batchmethode en kan worden gebruikt om contextinformatie te verkrijgen over het batchproces.
return Database.getQueryLocator([…]): Deze regel geeft een QueryLocator
object terug dat door een SOQL-query wordt geproduceerd. Het stuk SOQL spreekt volgens mij voor zich en zal in ieder geval anders zijn.
public void execute(Database.BatchableContext bc, List scope) {
List emails = new List();
for (Evenement_bezoeker__c bezoeker : (List) scope) {
if (bezoeker.Contact__c != null && bezoeker.Contact__r.Email != null) {
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setToAddresses(new String[] { bezoeker.Contact__r.Email });
email.setTemplateId(emailTemplateId);
email.setTargetObjectId(bezoeker.Contact__c);
email.setWhatId(bezoeker.Id); // Stel de WhatId in op het Evenement_bezoeker__c record
email.setSaveAsActivity(true); // Zorg ervoor dat de e-mail zelf wordt gelogd als een activiteit
emails.add(email);
}
}
Messaging.sendEmail(emails);
}
Batch Framework: Execute
Deexecute
methode is de tweede methode die verplicht is voor de Database.Batchable<SObject>
interface. Deze methode voert de hoofdlogica uit voor elke batch van records.
- Database.BatchableContext bc: Net als hierboven beschreven is dit het contextobject dat door Salesforce wordt doorgegeven aan de batchmethode.
- List<SObject> scope: Een lijst van Salesforce-objecten die in deze batch moeten worden verwerkt. In ons geval zijn dit de
Evenement_bezoeker__c
objecten. - List<Messaging.SingleEmailMessage> emails: Een lijst om de e-mailberichten op te slaan die verzonden moeten worden.
- Vervolgens wordt er met een for-loop door alle Evenement_bezoeker__c objecten gegaan en wordt de scope lijst gecast naar een lijst van Evenement_bezoeker__c objecten. Met het if-statement wordt er nog een extra check gedaan dat het gerelateerde contact en het e-mailadres van het contact niet null zijn.
- Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage(): Maakt een nieuw e-mailbericht aan.
- email.setToAddresses(new String[] { bezoeker.Contact__r.Email }): Stelt het “To” e-mailadres in op het e-mailadres van het gerelateerde contact.
- email.setTemplateId(emailTemplateId): Stelt het e-mailsjabloon ID in op de
emailTemplateId
die aan de klasse is doorgegeven (via de gemaakte flow). - email.setTargetObjectId(bezoeker.Contact__c): Stelt het doelobject ID in op het gerelateerde contact.
- email.setWhatId(bezoeker.Id): Stelt het
WhatId
in op hetEvenement_bezoeker__c
record. Dit koppelt de e-mailactiviteit aan het evenementbezoeker record en gebruiken we om de merge-tags van dit object te kunnen gebruiken in de e-mailtemplate. - email.setSaveAsActivity(true): Zorgt ervoor dat de e-mail wordt gelogd als een activiteit op het contact- en evenementbezoeker record.
- emails.add(email): Voegt het e-mailbericht toe aan de lijst van e-mailberichten die verzonden moeten worden.
- Messaging.sendEmail(emails): Verstuurt de e-mails die in de lijst
emails
zijn verzameld.
public void finish(Database.BatchableContext bc) {
// Eventuele opruimtaken
}
public class EmailBatchRequest {
@InvocableVariable
public List bezoekers;
@InvocableVariable
public Id emailTemplateId;
}
@InvocableMethod
public static void startBatch(List requests) {
for (EmailBatchRequest req : requests) {
BatchEmailReminderSender batch = new BatchEmailReminderSender(req.bezoekers, req.emailTemplateId);
Database.executeBatch(batch, 200);
}
}
}
Batch Framework: Finish
- Dit is de
finish
methode, een van de verplichte methoden voor een klasse die deDatabase.Batchable<SObject>
interface implementeert. De klasse is momenteel leeg omdat er geen specifieke opruimtaken of aanvullende logica nodig zijn na voltooiing van de batchverwerking. - public class EmailBatchRequest: Deze klasse is een container voor de parameters die nodig zijn om een batchverwerking te starten. De 2 variabelen die via de flow meegestuurd zijn, worden hier als @InvocableVariable weggeschreven, waardoor ze kunnen worden ingesteld via processen, zoals Flow in Salesforce.
- @InvocableMethod: Deze annotatie maakt de methode toegankelijk voor invocable processen zoals Flow in Salesforce (vergelijkbaar met de variabele).
- for (EmailBatchRequest req : requests): Itereert door elk
EmailBatchRequest
object in derequests
lijst. - BatchEmailReminderSender batch = new BatchEmailReminderSender(req.bezoekers, req.emailTemplateId): Maakt een nieuwe instantie van de
BatchEmailReminderSender
klasse met de bezoekerslijst en het e-mailsjabloon ID uit het verzoek. - Database.executeBatch(batch, 200): Start de batchverwerking met de aangemaakte
BatchEmailReminderSender
instantie. De tweede parameter200
geeft aan dat de batch in sets van 200 records moet worden verwerkt. - for (EmailBatchRequest req : requests): Itereert door elk EmailBatchRequest object in de requests lijst. Daarna wordt er een nieuwe instantie aangemaakt van de BatchEmailReminderSender klasse met de bezoekerslijst en het e-mailsjabloon ID uit het verzoek. Vervolgens start de batchverwerking in batches van 200 per keer.
Conclusie
Omdat Salesforce Flow een limiet van maximaal 5 e-mailberichten per keer hanteert, kun je geen Mass E-mail sturen via Salesforce Flow. Daarom heb ik de meeste stappen wel in Flow gebouwd, omdat dat sneller is, maar het daadwerkelijk verzenden van de E-mails wordt afgehandeld via APEX. Op deze manier combineer je een “Best of Breed” qua tooling.