You need to sign in to do that
Don't have an account?
Mee Sharma
Merge accounts using Apex with duplicate value in a particular field
I have a requirement where i need to merge all accounts if a particular field vendor_code__c value is duplicate.There is also a condition that the account of record type 'A' needs to be the master and record type 'B' needs to be the dupliacte. I have written a batch code for this fucntionality.I am not getting any errors but the accounts are not getting merged either. Kindly help!
global class BatchVendorAccountMerge implements database.Batchable<sobject> {
global database.QueryLocator start(Database.BatchableContext ctx){
string query;
query = 'SELECT Id, Type, RecordTypeId,Record_Type__c, Name, MasterRecordId, Vendor_Code__c FROM Account';
return database.getQuerylocator(query);
}
global void execute(Database.BatchableContext BC, list<account> scope ){
//create a map with vendor code and its account
//to store all unique vendor codes
Set<string> strVC = new Set<string>();
//create a map with vendor code and its account
map<string,list<account>> vendoraccmap = new map<string,list<account>>();
for(account a:scope){
strVC.add(a.Vendor_Code__c);
if(vendoraccmap.containskey(a.Vendor_Code__c)) {
vendoraccmap.get(a.Vendor_Code__c).add(a);
} else {
vendoraccmap.put(a.Vendor_Code__c, new List<account> {a});
}
}
system.debug('****Unique vendor codes***'+strVC);
system.debug('****vendor and acc map***'+vendoraccmap);
Account masteracc = new account();
list<account> dupacc = new list<account>();
for(string v:vendoraccmap.keySet()){
if(vendoraccmap.get(v).size() > 1)
system.debug('**vendoraccmapsize**'+vendoraccmap.get(v).size());
{
for(Account a:vendoraccmap.get(v))
{
if(a.Record_Type__c == 'A'){
masteracc.id=a.id;
}
else if (a.Record_Type__c == 'B') {
dupacc.add(a);
}
}
system.debug('***Master account***'+masteracc);
system.debug('***Duplicate accounts***'+dupacc);
}
}
Database.MergeResult[] results = Database.merge(masteracc, dupacc, false);
system.debug('***results merged**'+results);
for(Database.MergeResult res : results) {
if (res.isSuccess()) {
System.debug('Master record ID: ' + res.getId());
System.assertEquals(masteracc.Id, res.getId());
List<Id> mergedIds = res.getMergedRecordIds();
System.debug('IDs of merged records: ' + mergedIds);
}
else {
for(Database.Error err : res.getErrors()) {
System.debug(err.getMessage());
}
}
}
}
global void finish(Database.BatchableContext BC){
}
}
global class BatchVendorAccountMerge implements database.Batchable<sobject> {
global database.QueryLocator start(Database.BatchableContext ctx){
string query;
query = 'SELECT Id, Type, RecordTypeId,Record_Type__c, Name, MasterRecordId, Vendor_Code__c FROM Account';
return database.getQuerylocator(query);
}
global void execute(Database.BatchableContext BC, list<account> scope ){
//create a map with vendor code and its account
//to store all unique vendor codes
Set<string> strVC = new Set<string>();
//create a map with vendor code and its account
map<string,list<account>> vendoraccmap = new map<string,list<account>>();
for(account a:scope){
strVC.add(a.Vendor_Code__c);
if(vendoraccmap.containskey(a.Vendor_Code__c)) {
vendoraccmap.get(a.Vendor_Code__c).add(a);
} else {
vendoraccmap.put(a.Vendor_Code__c, new List<account> {a});
}
}
system.debug('****Unique vendor codes***'+strVC);
system.debug('****vendor and acc map***'+vendoraccmap);
Account masteracc = new account();
list<account> dupacc = new list<account>();
for(string v:vendoraccmap.keySet()){
if(vendoraccmap.get(v).size() > 1)
system.debug('**vendoraccmapsize**'+vendoraccmap.get(v).size());
{
for(Account a:vendoraccmap.get(v))
{
if(a.Record_Type__c == 'A'){
masteracc.id=a.id;
}
else if (a.Record_Type__c == 'B') {
dupacc.add(a);
}
}
system.debug('***Master account***'+masteracc);
system.debug('***Duplicate accounts***'+dupacc);
}
}
Database.MergeResult[] results = Database.merge(masteracc, dupacc, false);
system.debug('***results merged**'+results);
for(Database.MergeResult res : results) {
if (res.isSuccess()) {
System.debug('Master record ID: ' + res.getId());
System.assertEquals(masteracc.Id, res.getId());
List<Id> mergedIds = res.getMergedRecordIds();
System.debug('IDs of merged records: ' + mergedIds);
}
else {
for(Database.Error err : res.getErrors()) {
System.debug(err.getMessage());
}
}
}
}
global void finish(Database.BatchableContext BC){
}
}
I've changed your code a bit but couldn't test it. Here are some considerations about the changes:
1 - Your batch was not completely bulkified as the merge operation was not isolated by master Account.
2 - Keep in mind that the merge takes up two records to merge with a master record. In the code below I check whether this limit was reached to prevent any errors. In the next execution of the Batch the merge should continue without problems.
3 - If there are more than two record types in the Account you should consider adding a where clause to your query filtering by the ones you need. Preferably using the record type id.
4 - If you use the field Record_Type__c only on this occasion, you should consider removing it and access the Name or DeveloperName from the query. SELECT Id, Type, RecordTypeId, Record.DeveloperName or RecordType.Name, Name, MasterRecordId, Vendor_Code__c FROM Account.
5 - As the merge operation counts against the DML limit, I'm also checking it every time a merge is finished. If the limit is reached the execution is stopped and should continue without problems in the next execution of the Batch.
6 - Consider following the Java naming convention as per the recommendation of Salesforce.
7- Consider creating an Event Log for the asynchronous processes to make easier to track issues.
Hope to have helped!
Regards.
Don't forget to mark your thread as 'SOLVED' with the answer that best helps you.
All Answers
I've changed your code a bit but couldn't test it. Here are some considerations about the changes:
1 - Your batch was not completely bulkified as the merge operation was not isolated by master Account.
2 - Keep in mind that the merge takes up two records to merge with a master record. In the code below I check whether this limit was reached to prevent any errors. In the next execution of the Batch the merge should continue without problems.
3 - If there are more than two record types in the Account you should consider adding a where clause to your query filtering by the ones you need. Preferably using the record type id.
4 - If you use the field Record_Type__c only on this occasion, you should consider removing it and access the Name or DeveloperName from the query. SELECT Id, Type, RecordTypeId, Record.DeveloperName or RecordType.Name, Name, MasterRecordId, Vendor_Code__c FROM Account.
5 - As the merge operation counts against the DML limit, I'm also checking it every time a merge is finished. If the limit is reached the execution is stopped and should continue without problems in the next execution of the Batch.
6 - Consider following the Java naming convention as per the recommendation of Salesforce.
7- Consider creating an Event Log for the asynchronous processes to make easier to track issues.
Hope to have helped!
Regards.
Don't forget to mark your thread as 'SOLVED' with the answer that best helps you.
"**results merged** (Database.MergeResult[getErrors=(Database.Error[getFields=(Record_Type__c);getMessage=Unable to create/update fields: Record_Type__c. Please check the security settings of this field and verify that it is read/write for your profile or permission set.;getStatusCode=INVALID_FIELD_FOR_INSERT_UPDATE;]"
In this case, you can quit using a formula field, like I've shown in my previous answering, by querying information of the record type directly in the query or you can change the line 53 as shown below:
Hope to have helped!
Regards.
Don't forget to mark your thread as 'SOLVED' with the answer that best helps you.
your solution worked.Thank you.I just have one more doubt.I have tried this out myself but not sure on which part of the code i can implement this.
In my first post i had mentioned on how 'account of record type 'A' needs to be the master and record type 'B' needs to be the dupliacte',I also have one more condition where
In line 48, if accountstomerge contains only records of record type 'B' (no 'A' type records in the list),then the record with oldest created date needs to be the master and the rest records as 'Duplicate'. I have tried including the below code in line 62.BUt it doesnt seem to work at all.Kindly help!
if(masteraccount == null)
{
masteraccount = duplicatedAccounts[0];
for(integer i=0;i<=duplicatedAccounts.size();i++)
{
if(masteraccount.createddate < duplicatedAccounts[i].createddate){
masteraccount = duplicatedAccounts[i];
}
Your code is fine but you need to change your operator from less than(<) to greater than (>):
You could also implement you custom Iterator class but the way you did is also fine. I also would start my counter (Integer i=0) to start in 1, since the zero will always start as the master and by starting in 1 you avoid comparing the Account to itself.
Hope to have helped!
Regards.
Does this work to merge more than 2 accounts to a Master account? I have a requirement, I need to merge 275 accounts into one master account. Could the above batch process helps my requirement?