• apsullivan
  • NEWBIE
  • 20 Points
  • Member since 2013

  • Chatter
    Feed
  • 0
    Best Answers
  • 2
    Likes Received
  • 0
    Likes Given
  • 7
    Questions
  • 9
    Replies
We recently increased the size of a table in our back end by about tenfold and it's causing me to hit CPU limits in a batch callout job. I have attempted to rewrite the code to make several calls, but ultimately I think the failure comes in assembling the large map. Is there any way around this without adding pagination to our back end API call and then making a bunch of classes? I'd love to contain this in a single class but it's so much data being processed that I don't know how to get around this. Could I more efficiently parse the JSON maybe? Store it to a separate table and have two batch classes running, one to populate that table with values mirroring our back end and another to do the sync?

Original Code:
global class AdminAPIAccountLookupSync_Batch implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {

	global Database.QueryLocator start(Database.BatchableContext bc) {
        return database.getQuerylocator([SELECT Id,Client_ID__c,Dash_Id__c FROM Account WHERE RecordType.Name = 'End User' OR RecordType.Name = 'Agency / Channel Partner']);
	}

    global void execute(Database.batchableContext info, List<Account> scope) {
        //Establish Lists and Maps for later
        Map<String,String> mapIdMatch = new Map<String,String>();
        List<Account> listAccountsToUpdate = new List<Account>();

        try {
            String endpointAddition = '/clients';

            // refer to helper class to complete callout
            HttpResponse res = APICalloutHelper.getResponse(APICalloutHelper.buildAdminAPIGetRequest(endpointAddition));

            // parse JSON to extract Icon URL
            JSONParser responseParser = JSON.createParser(res.getBody());

            String rowId;
            
            while (responseParser.nextToken() != null) {
                if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'id') {
                    responseParser.nextToken();
                    rowId = responseParser.getText();
                } else if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'salesforce_id') {
                    responseParser.nextToken();
                    if (responseParser.getText() != 'null') {
                        mapIdMatch.put(responseParser.getText(),rowId);
                    }
                }
            }
            
        } catch (Exception e) {
            System.debug(LoggingLevel.ERROR,'Error getting Admin response: ' + e.getMessage());
        }
        if (!mapIdMatch.isEmpty()) {
            for (Account acc : scope) {
                if (mapIdMatch.containsKey(acc.Client_ID__c)) {
                    acc.Dash_Id__c = mapIdMatch.get(acc.Client_ID__c);
                    listAccountsToUpdate.add(acc);
                }
            }
            update listAccountsToUpdate;
        }
    }

	global void finish(Database.batchableContext info){}
}


Rework Attempt (also hitting CPU limit, but maybe I'm on the right path?)
global class AdminAPIAccountLookupSync_Batch implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {

    public Map<String,String> mapIdMatch = new Map<String,String>();
    public List<Account> listAccounts = new List<Account>();

	global Database.QueryLocator start(Database.BatchableContext bc) {
        return database.getQuerylocator([SELECT Id,Client_ID__c,Dash_Id__c,API_Ref__c FROM Account WHERE RecordType.Name = 'End User' OR RecordType.Name = 'Agency / Channel Partner']);
	}

    global void execute(Database.batchableContext info, List<Account> scope) {
        for (Account acc : scope) {
            listAccounts.add(acc);
        }
    }

	global void finish(Database.batchableContext info) {
        Integer page = 1;
        while (page != null) {
            try {
                String endpointAddition = '/clients?rows_per_page=1000&page=' + String.valueOf(page);
                // refer to helper class to complete callout
                HttpResponse res = APICalloutHelper.getResponse(APICalloutHelper.buildAdminAPIGetRequest(endpointAddition));

                // parse JSON to extract Icon URL
                JSONParser responseParser = JSON.createParser(res.getBody());

                if (res.getBody() != null) {
                    String rowId;
                    
                    while (responseParser.nextToken() != null) {
                        if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'id') {
                            responseParser.nextToken();
                            rowId = responseParser.getText();
                        } else if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'salesforce_id') {
                            responseParser.nextToken();
                            if (responseParser.getText() != 'null') {
                                mapIdMatch.put(responseParser.getText(),rowId);
                            }
                        }
                    }
                    page++;
                } else {
                    page = null;
                }
            } catch (Exception e) {
                page = null;
                System.debug(LoggingLevel.ERROR,'Error getting Admin response: ' + e.getMessage());
            }
        }

        List<Account> listAccountsToUpdate = new List<Account>();
        if (!mapIdMatch.isEmpty()) {
            for (Account acc : listAccounts) {
                if (mapIdMatch.containsKey(acc.Client_ID__c) {
                    acc.Dash_Id__c = mapIdMatch.get(acc.Client_ID__c);
                    listAccountsToUpdate.add(acc);
                }
            }
            update listAccountsToUpdate;
        }
    }
}

 
I have a trigger on a custom object called Launch Checklist that is supposed to use a unique identifier to look up another custom object called App and that App's Account based on an ID field.  When I edit and save a single record, everything works swimmingly.  When I try to force a bulk upload (through Data Loader or Anonymous Apex), the Trigger just doesn't seem to fire at all, or worse, it associates the wrong app/account.

Again, though, if I save a single record with the wrong info, without making any changes, the association occurs correctly.  I've spent time to ensure my trigger is bulkified.  Can anyone help identify what I'm doing wrong?  Code is below.

trigger LaunchChecklist on Launch_Checklist__c (before insert, before update) {
    
    String appId = Null;
    Map <String, App__c> mapApp = new Map <String, App__c> ();
    
    for(Launch_Checklist__c lc: Trigger.new) {
        if (lc.Apple_ID__c != Null) {
            appId = lc.Apple_ID__c;
        } else if (lc.Package_Name__c != Null) {
            appId = lc.Package_Name__c;
        }
    }

    List<App__c> apps = new List<App__c>([select App_ID__c, Account__c from App__c where App_ID__c = :appId limit 1]);
    
    if (apps.size() > 0) {
        for (App__c app: apps)  {
        	mapApp.put(app.App_ID__c, app);
    	}
        for(Launch_Checklist__c lc: Trigger.new) {
            lc.Account__c = mapApp.get(appId).Account__c;
            lc.App__c = mapApp.get(appId).Id;
    	}
    }
}


I would like to create a Visualforce page which allows a user to create a Case and multiple child records (called Case Campaigns) at once.

The problem I am trying to solve is that we want to use Cases to setup new campaigns, and have it be only one case per setup, but be able to define multiple budgets for each device.  So for example, one case for a setup, but 4 devices with 4 different budgets.  The best way I can think to achieve this is by using child records on the cases to represent the specific details on a granular level, but creating them one-by-one is time-consuming and tedious.  I want the user to be able to create them all on one page along with the new case.

Ideally, the flow works as follows:
  1. User clicks on a button to create a new case which redirects to this VF page
  2. The Case has 4-5 "core" fields that are just Case fields as I've defined them
  3. The user selects a number from a Picklist Field called "Number of Campaigns"
    1. Eg. The user selects the number "5" in the Picklist
  4. The page then reloads a block that shows the number of Case Campaigns listed as line items
    1. Eg. There are now 5 lines, each with a device and a budget
  5. The user performs a save, which inserts the new Case and the new Case Campaigns at once
Is this possible?  It seems a bit wacky to create a parent and child at once, so I am worried that it is not possible.  An alternative would be to just have the user create the case, then have a "New Case Campaigns" button which again redirects to a VF page, and which allows the user to again choose a dynamic number of Case Campaigns to create, then insert multiple at once.

I would love a code example if anyone has written a blog post anywhere or has faced such a challenge in the past.  Thanks very much in advance!
I'm not sure if I'm going about this the correct way, but I am trying to pull a list of lead email addresses with email domains that match the domains of contacts that are current clients.  The way I figured I'd go about this is by executing Anonymous Apex to send myself an email attachment.  The Anonymous Apex runs, but it hangs at "Unpacking results" in the Developer Console and at "Loading..." in the Workbench environment.  Am I going about this the wrong way?  I ran a similar query before and it worked great, but we have over 100k lead records so I am worried that it's just too much data.

Here's the code I am executing:

String finalList = 'Lead Email' + '\n';
Set<String> accountIds = new Set<String>();
Set<String> clientDomains = new Set<String>();
Set<String> leadEmails = new Set<String>();
List<Account> currentClientAccounts = [SELECT Id FROM Account WHERE Total_Won_Opportunities__c > 0];

for (Account a: currentClientAccounts){
     accountIds.add(a.Id);
}

List<Contact> currentClientContacts = [SELECT Id,Email_Domain__c FROM Contact WHERE AccountId IN :accountIds];

for (Contact c: currentClientContacts){
     clientDomains.add(c.Email_Domain__c);
}

List<Lead> leadList = [SELECT Email FROM Lead WHERE Email_Domain__c IN :clientDomains];

for (Lead leads: leadList){
     leadEmails.add(leads.Email);
}

for (String s: leadEmails){
     string line = s + '\n';
     finalList += line;
}

Messaging.EmailFileAttachment csvAttc = new Messaging.EmailFileAttachment();
blob csvBlob = Blob.valueOf(finalList);
string csvname= 'LeadEmailsMatchingClientDomains.xls';
csvAttc.setFileName(csvname);
csvAttc.setBody(csvBlob);
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
String[] toAddresses = new list<string> {'My Email Address'}; //this would be my actual email address in the real code
String subject = 'Lead Emails CSV';
email.setSubject(subject);
email.setToAddresses( toAddresses );
email.setPlainTextBody('Lead Emails CSV ');
email.setFileAttachments(new Messaging.EmailFileAttachment[]{csvAttc});
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

Hi - I'm having an issue with a very basic Visualforce page that has some iFramed reports in tabs.  Before the Winter release, links to Salesforce objects from within these iFramed reports worked decently.  As an example, if a user clicked a link to an account record, he or she would be brought to that account record page within the iFrame.  It wasn't totally ideal, but not bad.  Now, however, the links take no action when clicked.  Nothing happens at all.

 

My question is: is there a better way to get reports into Visualforce?  If not, is there some way to get these links to behave more appropriately?

I'm new to development and I've written a trigger to update the Account with a field when a) the field is blank and b) a Contact is created or updated and contains a value in a corresponding field.  The idea being to maintain the data in two places.  However, I'm getting an exception which references "kugadd" which I assume is from the Kugamon package we have installed.  Can anyone help?

 

Here is my trigger:

 

trigger OnAccountContactCategory on Contact (before insert, before update) {

    List<Id> AccountsToUpdate = new List<Id>{}; 
    // Find account to update
    for(Contact c: Trigger.new){
        if (c.Categorycontact__c != Null && c.AccountId != Null)  {
              AccountsToUpdate.add(c.AccountId);
        }
        // Pull field data from account
      List<Account> accts = new List<Account>([SELECT Id, Category__c FROM Account WHERE Id IN :AccountsToUpdate]);
        // Update account    
      for(Account a: accts) {
         if (a.Category__c == Null) {
                a.Category__c = c.Categorycontact__c;
              update a;
         }
      }
   }
}

 

Here is the emailed exception (I removed the IDs themselves):

 

 

OnAccountContactCategory: execution of BeforeUpdate

caused by: System.DmlException: Update failed. First exception on row 0 with id [ACCOUNT ID]; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, kugadd.AccountAfterInsertAfterUpdate: execution of AfterUpdate

caused by: System.DmlException: Update failed. First exception on row 0 with id [CONTACT ID]; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = [CONTACT ID]) is currently in trigger OnAccountContactCategory, therefore it cannot recursively update itself: []

(kugadd)
: []

Trigger.OnAccountContactCategory: line 15, column 1

 

Thoughts?  Thanks!

I've written a pretty simple trigger and I was hoping to get a hand with a test class.

 

All my trigger does is stamp the account with the same user lookup value from an opportunity (CF Manager -> Account CF Owner fields).  I'm brand new to Apex so help is always appreciated.  The trigger fires without issue.

 

Here's the trigger:

 

trigger OnAccountOpportunityCFManager on Opportunity (before insert, before update) {
    List<Id> AccountsToUpdate = new List<Id>{}; 
    // Find account to update
    for(Opportunity o: Trigger.new){
        if (o.CF_Manager__c != Null && o.AccountId != Null)  {
            AccountsToUpdate.add(o.AccountId);
        }
    // Pull field data from account
    List<Account> accts = new List<Account>([SELECT Id, Account_CF_Owner__c FROM Account WHERE Id IN :AccountsToUpdate]);
    // Update account    
    for(Account a: accts) {
    a.Account_CF_Owner__c = o.CF_Manager__c;
    update a;
    }
    }
}

 Thanks!

I would like to create a Visualforce page which allows a user to create a Case and multiple child records (called Case Campaigns) at once.

The problem I am trying to solve is that we want to use Cases to setup new campaigns, and have it be only one case per setup, but be able to define multiple budgets for each device.  So for example, one case for a setup, but 4 devices with 4 different budgets.  The best way I can think to achieve this is by using child records on the cases to represent the specific details on a granular level, but creating them one-by-one is time-consuming and tedious.  I want the user to be able to create them all on one page along with the new case.

Ideally, the flow works as follows:
  1. User clicks on a button to create a new case which redirects to this VF page
  2. The Case has 4-5 "core" fields that are just Case fields as I've defined them
  3. The user selects a number from a Picklist Field called "Number of Campaigns"
    1. Eg. The user selects the number "5" in the Picklist
  4. The page then reloads a block that shows the number of Case Campaigns listed as line items
    1. Eg. There are now 5 lines, each with a device and a budget
  5. The user performs a save, which inserts the new Case and the new Case Campaigns at once
Is this possible?  It seems a bit wacky to create a parent and child at once, so I am worried that it is not possible.  An alternative would be to just have the user create the case, then have a "New Case Campaigns" button which again redirects to a VF page, and which allows the user to again choose a dynamic number of Case Campaigns to create, then insert multiple at once.

I would love a code example if anyone has written a blog post anywhere or has faced such a challenge in the past.  Thanks very much in advance!

I've written a pretty simple trigger and I was hoping to get a hand with a test class.

 

All my trigger does is stamp the account with the same user lookup value from an opportunity (CF Manager -> Account CF Owner fields).  I'm brand new to Apex so help is always appreciated.  The trigger fires without issue.

 

Here's the trigger:

 

trigger OnAccountOpportunityCFManager on Opportunity (before insert, before update) {
    List<Id> AccountsToUpdate = new List<Id>{}; 
    // Find account to update
    for(Opportunity o: Trigger.new){
        if (o.CF_Manager__c != Null && o.AccountId != Null)  {
            AccountsToUpdate.add(o.AccountId);
        }
    // Pull field data from account
    List<Account> accts = new List<Account>([SELECT Id, Account_CF_Owner__c FROM Account WHERE Id IN :AccountsToUpdate]);
    // Update account    
    for(Account a: accts) {
    a.Account_CF_Owner__c = o.CF_Manager__c;
    update a;
    }
    }
}

 Thanks!

We recently increased the size of a table in our back end by about tenfold and it's causing me to hit CPU limits in a batch callout job. I have attempted to rewrite the code to make several calls, but ultimately I think the failure comes in assembling the large map. Is there any way around this without adding pagination to our back end API call and then making a bunch of classes? I'd love to contain this in a single class but it's so much data being processed that I don't know how to get around this. Could I more efficiently parse the JSON maybe? Store it to a separate table and have two batch classes running, one to populate that table with values mirroring our back end and another to do the sync?

Original Code:
global class AdminAPIAccountLookupSync_Batch implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {

	global Database.QueryLocator start(Database.BatchableContext bc) {
        return database.getQuerylocator([SELECT Id,Client_ID__c,Dash_Id__c FROM Account WHERE RecordType.Name = 'End User' OR RecordType.Name = 'Agency / Channel Partner']);
	}

    global void execute(Database.batchableContext info, List<Account> scope) {
        //Establish Lists and Maps for later
        Map<String,String> mapIdMatch = new Map<String,String>();
        List<Account> listAccountsToUpdate = new List<Account>();

        try {
            String endpointAddition = '/clients';

            // refer to helper class to complete callout
            HttpResponse res = APICalloutHelper.getResponse(APICalloutHelper.buildAdminAPIGetRequest(endpointAddition));

            // parse JSON to extract Icon URL
            JSONParser responseParser = JSON.createParser(res.getBody());

            String rowId;
            
            while (responseParser.nextToken() != null) {
                if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'id') {
                    responseParser.nextToken();
                    rowId = responseParser.getText();
                } else if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'salesforce_id') {
                    responseParser.nextToken();
                    if (responseParser.getText() != 'null') {
                        mapIdMatch.put(responseParser.getText(),rowId);
                    }
                }
            }
            
        } catch (Exception e) {
            System.debug(LoggingLevel.ERROR,'Error getting Admin response: ' + e.getMessage());
        }
        if (!mapIdMatch.isEmpty()) {
            for (Account acc : scope) {
                if (mapIdMatch.containsKey(acc.Client_ID__c)) {
                    acc.Dash_Id__c = mapIdMatch.get(acc.Client_ID__c);
                    listAccountsToUpdate.add(acc);
                }
            }
            update listAccountsToUpdate;
        }
    }

	global void finish(Database.batchableContext info){}
}


Rework Attempt (also hitting CPU limit, but maybe I'm on the right path?)
global class AdminAPIAccountLookupSync_Batch implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {

    public Map<String,String> mapIdMatch = new Map<String,String>();
    public List<Account> listAccounts = new List<Account>();

	global Database.QueryLocator start(Database.BatchableContext bc) {
        return database.getQuerylocator([SELECT Id,Client_ID__c,Dash_Id__c,API_Ref__c FROM Account WHERE RecordType.Name = 'End User' OR RecordType.Name = 'Agency / Channel Partner']);
	}

    global void execute(Database.batchableContext info, List<Account> scope) {
        for (Account acc : scope) {
            listAccounts.add(acc);
        }
    }

	global void finish(Database.batchableContext info) {
        Integer page = 1;
        while (page != null) {
            try {
                String endpointAddition = '/clients?rows_per_page=1000&page=' + String.valueOf(page);
                // refer to helper class to complete callout
                HttpResponse res = APICalloutHelper.getResponse(APICalloutHelper.buildAdminAPIGetRequest(endpointAddition));

                // parse JSON to extract Icon URL
                JSONParser responseParser = JSON.createParser(res.getBody());

                if (res.getBody() != null) {
                    String rowId;
                    
                    while (responseParser.nextToken() != null) {
                        if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'id') {
                            responseParser.nextToken();
                            rowId = responseParser.getText();
                        } else if (responseParser.getCurrentToken() == JSONToken.FIELD_NAME && responseParser.getText() == 'salesforce_id') {
                            responseParser.nextToken();
                            if (responseParser.getText() != 'null') {
                                mapIdMatch.put(responseParser.getText(),rowId);
                            }
                        }
                    }
                    page++;
                } else {
                    page = null;
                }
            } catch (Exception e) {
                page = null;
                System.debug(LoggingLevel.ERROR,'Error getting Admin response: ' + e.getMessage());
            }
        }

        List<Account> listAccountsToUpdate = new List<Account>();
        if (!mapIdMatch.isEmpty()) {
            for (Account acc : listAccounts) {
                if (mapIdMatch.containsKey(acc.Client_ID__c) {
                    acc.Dash_Id__c = mapIdMatch.get(acc.Client_ID__c);
                    listAccountsToUpdate.add(acc);
                }
            }
            update listAccountsToUpdate;
        }
    }
}

 
I have a trigger on a custom object called Launch Checklist that is supposed to use a unique identifier to look up another custom object called App and that App's Account based on an ID field.  When I edit and save a single record, everything works swimmingly.  When I try to force a bulk upload (through Data Loader or Anonymous Apex), the Trigger just doesn't seem to fire at all, or worse, it associates the wrong app/account.

Again, though, if I save a single record with the wrong info, without making any changes, the association occurs correctly.  I've spent time to ensure my trigger is bulkified.  Can anyone help identify what I'm doing wrong?  Code is below.

trigger LaunchChecklist on Launch_Checklist__c (before insert, before update) {
    
    String appId = Null;
    Map <String, App__c> mapApp = new Map <String, App__c> ();
    
    for(Launch_Checklist__c lc: Trigger.new) {
        if (lc.Apple_ID__c != Null) {
            appId = lc.Apple_ID__c;
        } else if (lc.Package_Name__c != Null) {
            appId = lc.Package_Name__c;
        }
    }

    List<App__c> apps = new List<App__c>([select App_ID__c, Account__c from App__c where App_ID__c = :appId limit 1]);
    
    if (apps.size() > 0) {
        for (App__c app: apps)  {
        	mapApp.put(app.App_ID__c, app);
    	}
        for(Launch_Checklist__c lc: Trigger.new) {
            lc.Account__c = mapApp.get(appId).Account__c;
            lc.App__c = mapApp.get(appId).Id;
    	}
    }
}


I'm not sure if I'm going about this the correct way, but I am trying to pull a list of lead email addresses with email domains that match the domains of contacts that are current clients.  The way I figured I'd go about this is by executing Anonymous Apex to send myself an email attachment.  The Anonymous Apex runs, but it hangs at "Unpacking results" in the Developer Console and at "Loading..." in the Workbench environment.  Am I going about this the wrong way?  I ran a similar query before and it worked great, but we have over 100k lead records so I am worried that it's just too much data.

Here's the code I am executing:

String finalList = 'Lead Email' + '\n';
Set<String> accountIds = new Set<String>();
Set<String> clientDomains = new Set<String>();
Set<String> leadEmails = new Set<String>();
List<Account> currentClientAccounts = [SELECT Id FROM Account WHERE Total_Won_Opportunities__c > 0];

for (Account a: currentClientAccounts){
     accountIds.add(a.Id);
}

List<Contact> currentClientContacts = [SELECT Id,Email_Domain__c FROM Contact WHERE AccountId IN :accountIds];

for (Contact c: currentClientContacts){
     clientDomains.add(c.Email_Domain__c);
}

List<Lead> leadList = [SELECT Email FROM Lead WHERE Email_Domain__c IN :clientDomains];

for (Lead leads: leadList){
     leadEmails.add(leads.Email);
}

for (String s: leadEmails){
     string line = s + '\n';
     finalList += line;
}

Messaging.EmailFileAttachment csvAttc = new Messaging.EmailFileAttachment();
blob csvBlob = Blob.valueOf(finalList);
string csvname= 'LeadEmailsMatchingClientDomains.xls';
csvAttc.setFileName(csvname);
csvAttc.setBody(csvBlob);
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
String[] toAddresses = new list<string> {'My Email Address'}; //this would be my actual email address in the real code
String subject = 'Lead Emails CSV';
email.setSubject(subject);
email.setToAddresses( toAddresses );
email.setPlainTextBody('Lead Emails CSV ');
email.setFileAttachments(new Messaging.EmailFileAttachment[]{csvAttc});
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});

I'm new to development and I've written a trigger to update the Account with a field when a) the field is blank and b) a Contact is created or updated and contains a value in a corresponding field.  The idea being to maintain the data in two places.  However, I'm getting an exception which references "kugadd" which I assume is from the Kugamon package we have installed.  Can anyone help?

 

Here is my trigger:

 

trigger OnAccountContactCategory on Contact (before insert, before update) {

    List<Id> AccountsToUpdate = new List<Id>{}; 
    // Find account to update
    for(Contact c: Trigger.new){
        if (c.Categorycontact__c != Null && c.AccountId != Null)  {
              AccountsToUpdate.add(c.AccountId);
        }
        // Pull field data from account
      List<Account> accts = new List<Account>([SELECT Id, Category__c FROM Account WHERE Id IN :AccountsToUpdate]);
        // Update account    
      for(Account a: accts) {
         if (a.Category__c == Null) {
                a.Category__c = c.Categorycontact__c;
              update a;
         }
      }
   }
}

 

Here is the emailed exception (I removed the IDs themselves):

 

 

OnAccountContactCategory: execution of BeforeUpdate

caused by: System.DmlException: Update failed. First exception on row 0 with id [ACCOUNT ID]; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, kugadd.AccountAfterInsertAfterUpdate: execution of AfterUpdate

caused by: System.DmlException: Update failed. First exception on row 0 with id [CONTACT ID]; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = [CONTACT ID]) is currently in trigger OnAccountContactCategory, therefore it cannot recursively update itself: []

(kugadd)
: []

Trigger.OnAccountContactCategory: line 15, column 1

 

Thoughts?  Thanks!

I've written a pretty simple trigger and I was hoping to get a hand with a test class.

 

All my trigger does is stamp the account with the same user lookup value from an opportunity (CF Manager -> Account CF Owner fields).  I'm brand new to Apex so help is always appreciated.  The trigger fires without issue.

 

Here's the trigger:

 

trigger OnAccountOpportunityCFManager on Opportunity (before insert, before update) {
    List<Id> AccountsToUpdate = new List<Id>{}; 
    // Find account to update
    for(Opportunity o: Trigger.new){
        if (o.CF_Manager__c != Null && o.AccountId != Null)  {
            AccountsToUpdate.add(o.AccountId);
        }
    // Pull field data from account
    List<Account> accts = new List<Account>([SELECT Id, Account_CF_Owner__c FROM Account WHERE Id IN :AccountsToUpdate]);
    // Update account    
    for(Account a: accts) {
    a.Account_CF_Owner__c = o.CF_Manager__c;
    update a;
    }
    }
}

 Thanks!