Examples & Use Cases
Set Field Value in Merge
Basic Example Code
global class DefaultMergeGenericPlugin implements dupcheck.dc3PluginInterface {
// Define the events this plugin supports (only MERGE_SET_FIELD in this case)
private static Set<String> implementedEvents = new Set<String>{ 'MERGE_SET_FIELD' };
// Checks if the plugin is available for the given event type.
global Boolean isAvailable(dupcheck.dc3Plugin.PluginEventType eventType) {
return implementedEvents.contains(eventType.name());
}
// Executes the appropriate logic based on the event type.
global Object execute(dupcheck.dc3Plugin.PluginEventType eventType, Object eventData) {
// Only handle the MERGE_SET_FIELD event.
if (eventType.name() == 'MERGE_SET_FIELD') {
// Cast eventData to the appropriate input type and call mergeSetField.
return mergeSetField((dupcheck.dc3PluginModel.MergeSetFieldInput) eventData);
}
// Return null if the event type is not supported.
return null;
}
// Basic implementation: By default, return the field values provided by the merge rules.
public dupcheck.dc3PluginModel.MergeSetFieldOutput mergeSetField(dupcheck.dc3PluginModel.MergeSetFieldInput input) {
// Create an output object initialized with the input.
dupcheck.dc3PluginModel.MergeSetFieldOutput output = new dupcheck.dc3PluginModel.MergeSetFieldOutput(input);
return output;
}
}
Example 1: Pick a valid email from Plauti Verify
When using Plauti Verify to validate email addresses, every email address receives a status code. This plugin picks the email address with the highest validation status (312 over 314 over 313 over 311) - making sure the best possible email is chosen in the master record. If no status code is found, it falls back on the default merge rules.
global class DefaultGenericPlugin implements dupcheck.dc3PluginInterface {
private static Set<String> implementedEvents = new Set<String>{'MERGE_SET_FIELD'};
global Boolean isAvailable(dupcheck.dc3Plugin.PluginEventType eventType) {
return DefaultGenericPlugin.implementedEvents.contains(eventType.name());
}
global Object execute(dupcheck.dc3Plugin.PluginEventType eventType, Object eventData) {
switch on eventType {
when MERGE_SET_FIELD {
return this.mergeSetField((dupcheck.dc3PluginModel.MergeSetFieldInput) eventData);
}
when else {
return null;
}
}
}
public dupcheck.dc3PluginModel.MergeSetFieldOutput mergeSetField(dupcheck.dc3PluginModel.MergeSetFieldInput input) {
dupcheck.dc3PluginModel.MergeSetFieldOutput output = new dupcheck.dc3PluginModel.MergeSetFieldOutput(input);
// Only apply for Leads (objectPrefix '00Q')
if (input.objectPrefix == '00Q' && input.fieldSourceMap.containsKey('Email')) {
// Define hierarchy for recordval__rv2EmailStatusCode__c: 312, 314, 313, 311
List<Integer> hierarchy = new List<Integer>{312, 314, 313, 311};
Boolean found = false;
for (Integer code : hierarchy) {
for (Sobject record : input.objectDataList) {
Object statusVal = record.get('recordval__rv2EmailStatusCode__c');
if (statusVal != null && Integer.valueOf(String.valueOf(statusVal)) == code) {
output.fieldSourceMap.put('Email', record.Id);
found = true;
break;
}
}
if (found) {
break;
}
}
// If no matching record is found, default to the master record.
if (!found) {
output.fieldSourceMap.put('Email', input.masterRecordId);
}
}
return output;
}
}
Example 2: Pick a valid Website Address using regular expressions
In this example, we use a regular expression to determine the validity of the website value in Leads. It will pick the first valid website value it comes across and sets that value.
global class DefaultMergeGenericPlugin implements dupcheck.dc3PluginInterface {
// Supported event: MERGE_SET_FIELD
private static Set<String> implementedEvents = new Set<String>{ 'MERGE_SET_FIELD' };
global Boolean isAvailable(dupcheck.dc3Plugin.PluginEventType eventType) {
return implementedEvents.contains(eventType.name());
}
global Object execute(dupcheck.dc3Plugin.PluginEventType eventType, Object eventData) {
if (eventType.name() == 'MERGE_SET_FIELD') {
return mergeSetField((dupcheck.dc3PluginModel.MergeSetFieldInput) eventData);
}
return null;
}
// Implementation: For Leads (objectPrefix '00Q'), pick the first Website value that is valid per regex.
public dupcheck.dc3PluginModel.MergeSetFieldOutput mergeSetField(dupcheck.dc3PluginModel.MergeSetFieldInput input) {
dupcheck.dc3PluginModel.MergeSetFieldOutput output = new dupcheck.dc3PluginModel.MergeSetFieldOutput(input);
// Apply only for Leads (objectPrefix '00Q') and if the Website field is included in the merge.
if (input.objectPrefix == '00Q' && input.fieldSourceMap.containsKey('Website')) {
// Regex pattern to validate website URL (allows optional http/https)
// Note: In Apex, double backslashes are needed for escaping.
String websiteRegex = '^((https?)://)?[\\w\\.-]+\\.[a-zA-Z]{2,}$';
// Compile the regex pattern.
Pattern pattern = Pattern.compile(websiteRegex);
// Iterate over candidate records and pick the first valid Website.
for (Sobject record : input.objectDataList) {
String websiteValue = String.valueOf(record.get('Website'));
if (websiteValue != null) {
Matcher matcher = pattern.matcher(websiteValue);
if (matcher.matches()) {
// Assign the valid Website value by setting the record's Id.
output.fieldSourceMap.put('Website', record.Id);
break;
}
}
}
}
return output;
}
}
Example 3: Set the most recent updated field (using History Tracking)
This merge plugin ensures that during Lead merge operations, the master Lead record adopts the most recently updated Mobile Phone value. The plugin queries LeadHistory records (assuming field history tracking is enabled on the MobilePhone field) for all Lead records involved in the merge. It then compares the update timestamps and designates the record with the latest Mobile Phone update as the source for both the MobilePhone field and an optional tracking field. This guarantees that the master record retains the most current Mobile Phone information.
global class LeadMergeSetMobileByHistoryPlugin implements dupcheck.dc3PluginInterface {
// Define the event type this plugin handles.
private static Set<String> implementedEvents = new Set<String>{ 'MERGE_SET_FIELD' };
global Boolean isAvailable(dupcheck.dc3Plugin.PluginEventType eventType) {
return implementedEvents.contains(eventType.name());
}
global Object execute(dupcheck.dc3Plugin.PluginEventType eventType, Object eventData) {
// Only handle the MERGE_SET_FIELD event.
if (eventType.name() == 'MERGE_SET_FIELD') {
// Cast eventData to the appropriate input type and call mergeSetField.
return mergeSetField((dupcheck.dc3PluginModel.MergeSetFieldInput) eventData);
}
// Return null if the event type is not supported.
return null;
}
public dupcheck.dc3PluginModel.MergeSetFieldOutput mergeSetField(dupcheck.dc3PluginModel.MergeSetFieldInput input) {
// Initialize the output object with the input values (default selections).
dupcheck.dc3PluginModel.MergeSetFieldOutput output = new dupcheck.dc3PluginModel.MergeSetFieldOutput(input);
// 1. Only apply this logic for Lead merges ('00Q') and if MobilePhone is part of the merge.
// Checking fieldSourceMap ensures we only query history if the field is relevant to the merge setup.
if (input.objectPrefix == '00Q' && input.fieldSourceMap.containsKey('MobilePhone') && input.objectDataList != null && !input.objectDataList.isEmpty()) {
// 2. Collect Lead IDs from the candidate records
Set<Id> leadIds = new Set<Id>();
for (SObject record : input.objectDataList) {
leadIds.add(record.Id);
}
// 3. Perform a single bulk query to get the most recent MobilePhone update
// among all involved leads. We only need the latest one overall.
List<LeadHistory> historyList = new List<LeadHistory>();
try {
historyList = [
SELECT Id, Field, CreatedDate, LeadId
FROM LeadHistory
WHERE LeadId IN :leadIds AND Field = 'MobilePhone'
ORDER BY CreatedDate DESC
LIMIT 1 // We only care about the single most recent update across all candidates
];
} catch (Exception e) {
// Handle query exception (e.g., log it) - prevents plugin failure
System.debug(LoggingLevel.ERROR, 'Error querying LeadHistory for MobilePhone: ' + e.getMessage());
// Return default output if query fails
return output;
}
// 4. If a history record was found, use its LeadId to set the source
if (!historyList.isEmpty()) {
LeadHistory latestUpdate = historyList[0];
Id sourceLeadId = latestUpdate.LeadId; // ID of the Lead record that had the latest update
// Double-check if this Lead ID is actually in our input list
// (It always should be, but defensive coding doesn't hurt)
// We need the record.Id from the input list which equals sourceLeadId
if (leadIds.contains(sourceLeadId)) {
// Set the source for the MobilePhone field to the record ID
// that corresponds to the Lead with the latest update.
// NOTE: We are providing the *record Id* from the input list,
// telling DC to use the MobilePhone value *from that specific record*.
output.fieldSourceMap.put('MobilePhone', sourceLeadId);
System.debug(LoggingLevel.INFO, 'Setting MobilePhone source to record ' + sourceLeadId + ' based on latest history update at ' + latestUpdate.CreatedDate);
} else {
System.debug(LoggingLevel.WARN, 'Latest MobilePhone history update corresponds to Lead ' + sourceLeadId + ', which was not found in the input objectDataList. Using default selection.');
}
} else {
System.debug(LoggingLevel.INFO, 'No LeadHistory found for MobilePhone field for the merging leads. Using default selection.');
}
}
// Return the output object (either default or modified).
return output;
}
// No mergeFailed or afterMerge methods needed for dc3PluginInterface
}
Example 4: In a Merge, Set Lead Owner to User with Fewest Leads
This Duplicate Check plugin automatically sets the owner of the final merged Lead record. It looks at the users who owned the original duplicate Leads and checks which one currently has the lowest total number of Leads assigned to them in Salesforce. The merged Lead is then assigned to that user, helping to distribute workload more evenly.
global class SetOwnerByLeastLeadsPlugin implements dupcheck.dc3PluginInterface {
// Define the events this plugin supports
private static Set<String> implementedEvents = new Set<String>{ 'MERGE_SET_FIELD' };
global Boolean isAvailable(dupcheck.dc3Plugin.PluginEventType eventType) {
return implementedEvents.contains(eventType.name());
}
global Object execute(dupcheck.dc3Plugin.PluginEventType eventType, Object eventData) {
// Only handle the MERGE_SET_FIELD event.
if (eventType.name() == 'MERGE_SET_FIELD') {
// Cast eventData to the appropriate input type and call mergeSetField.
return mergeSetField((dupcheck.dc3PluginModel.MergeSetFieldInput) eventData);
}
// Return null if the event type is not supported.
return null;
}
public dupcheck.dc3PluginModel.MergeSetFieldOutput mergeSetField(dupcheck.dc3PluginModel.MergeSetFieldInput input) {
// Initialize the output object with the input values (default selections).
dupcheck.dc3PluginModel.MergeSetFieldOutput output = new dupcheck.dc3PluginModel.MergeSetFieldOutput(input);
// 1. Only apply this logic for Lead merges (Object Prefix '00Q')
if (input.objectPrefix == '00Q' && input.objectDataList != null && !input.objectDataList.isEmpty()) {
// 2. Collect unique Owner IDs from the candidate records
Set<Id> ownerIds = new Set<Id>();
for (SObject record : input.objectDataList) {
Id ownerId = (Id)record.get('OwnerId');
// Ensure OwnerId is not null before adding
if (ownerId != null) {
ownerIds.add(ownerId);
}
}
// Proceed only if we found owners in the candidate list
if (!ownerIds.isEmpty()) {
// 3. Use Aggregate Query to count total Leads per relevant owner
Map<Id, Integer> leadCountsByOwner = new Map<Id, Integer>();
try {
for (AggregateResult ar : [
SELECT COUNT(Id) leadCount, OwnerId
FROM Lead
WHERE OwnerId IN :ownerIds
GROUP BY OwnerId
]) {
leadCountsByOwner.put((Id)ar.get('OwnerId'), (Integer)ar.get('leadCount'));
}
} catch (Exception e) {
// Handle query exception (e.g., log it) - prevents plugin failure
System.debug(LoggingLevel.ERROR, 'Error querying Lead counts for owners: ' + e.getMessage());
// Optionally return default output if query fails
// return output;
}
// 4. Determine the Owner with the minimum Lead count from the map
Id targetOwnerId = null;
// Initialize with the maximum possible Integer value in Apex
Integer minLeadCount = 2147483647;
for (Id currentOwnerId : ownerIds) {
// Get count for this owner. If not in map (e.g., owns 0 leads), default to 0.
Integer currentCount = leadCountsByOwner.containsKey(currentOwnerId) ? leadCountsByOwner.get(currentOwnerId) : 0;
if (currentCount < minLeadCount) {
minLeadCount = currentCount;
targetOwnerId = currentOwnerId;
}
// Optional: Handle ties - current logic picks the one encountered first
// in the ownerIds set iteration based on internal Set ordering.
}
// 5. If a target owner was found, find a corresponding record in the input list
// and set its Id as the source for the OwnerId field.
if (targetOwnerId != null) {
for (SObject record : input.objectDataList) {
// Check OwnerId safely, avoid NullPointerException if OwnerId happens to be null on a record
Id recordOwnerId = (Id)record.get('OwnerId');
if (recordOwnerId != null && recordOwnerId == targetOwnerId) {
// Found a record owned by the target user.
// Set this record's Id as the source for the OwnerId field.
output.fieldSourceMap.put('OwnerId', record.Id);
System.debug(LoggingLevel.INFO, 'Setting OwnerId source to record ' + record.Id + ' (Owner: ' + targetOwnerId + ') based on lowest lead count (' + minLeadCount + ').');
break; // Found the source record, no need to check further
}
}
} else {
System.debug(LoggingLevel.INFO, 'Could not determine a target owner with the minimum lead count.');
}
} else {
System.debug(LoggingLevel.INFO, 'No owner IDs found among merge candidates.');
}
}
// Return the output object (either default or modified).
return output;
}
}
Updated 17 days ago