Reverb
Reverb

Reputation: 33

Update related records with an Apex trigger

I have the following use case and have trouble to write a correct trigger for it

On object Account we save our patients. Every patient has a connected general practitioner (GP). The general practitioners are saved in object Contact. Sometimes a GP is retiring, a new GP takes over the general practice so al connected patients to that GP must change to the new GP.

I made in Salesforce in object Contact 2 fields, GPChange__c (boolean) and NewGP__c (lookup field on contact). When a user set the boolean field to true and fill in the new GP, the Apex trigger must be run and collect all accounts(patients) connected to the 'retired' GP and update the field GeneralPracticioner__c on account with the id from NewGP__C.

This is how far i come, it will run and collect, but doesn't update the field. Can anyone plz help me?

 trigger trgnewGP on Contact (before update) {
    for (Contact mycontact : Trigger.new) {
        if (mycontact.GPChange__c = true && mycontact.NewGP__c != null){
            list <account> gps = [SELECT GeneralPracticioner__c from account
                                    where GeneralPracticioner__c =: mycontact.id];
            for(Account acc : gps){
            if (gps.size() > 0) {
                acc.GeneralPracticioner__c = mycontact.NewGP__c;
            
                    
                    } }
            }
    }
}

Upvotes: 1

Views: 9823

Answers (2)

RubenDG
RubenDG

Reputation: 1405

There are some issues in that code:

  • if (mycontact.GPChange__c = true ... - Beware: you are assigning true to GPChange__c. Keep in mind that there is no need to write checkboxField == true, it already resolves to a Boolean.
  • There are no dml, it will never update the accounts.
  • It isn't bulkified: you're doing a query inside a loop, so it may throw a LimitException (Too many SOQL queries: 101)

In order to pull out the query, you need to collect the Id of the retired GP.
Then you can retrieve the account records that need to be updated and set GeneralPracticioner__c using Trigger.NewMap.

trigger trgnewGP on Contact (before update) {
    List<Id> retiredGpIds = new List<Id>();
    for (Contact gp : Trigger.new) {
        if (gp.GPChange__c && gp.NewGP__c != null) {
            retiredGpIds.add(gp.Id);
        }
    }
    
    if (!retiredGpIds.isEmpty()) {
        List<Account> patients = [SELECT GeneralPracticioner__c from Account WHERE GeneralPracticioner__c IN :retiredGpIds];
        for (Account acc : patients) {
            acc.GeneralPracticioner__c = Trigger.NewMap.get(acc.GeneralPracticioner__c).NewGP__c;
        }
        update patients;
    }
}

Since there are no update to Contact records, I would run this trigger in after update.


Update:

The weird thing, when i try to use Dataloader [...] it works perfect and doesn't have any apex CPU errors.

Dataloader uses bulk api, which run in asynchronous context, therefore they have higher CPU Limit: 60s vs 10s.

I would suggest to extract the logic of that trigger in a future method in a trigger handler class.
It should look like:

@future
public static void updateGeneralPracticioner(List<Id> retiredGpIds) {
    Map<Id, Contact> gpMap = new Map<Id, Contact>([SELECT Id, NewGP__c FROM Contact WHERE Id IN :retiredGpIds);
    List<Account> patients = [SELECT GeneralPracticioner__c from Account WHERE GeneralPracticioner__c IN :retiredGpIds];
    for (Account acc : patients) {
        acc.GeneralPracticioner__c = gpMap.get(acc.GeneralPracticioner__c).NewGP__c;
    }
    update patients;
}

The trigger:

trigger trgnewGP on Contact (after update) {
    List<Id> retiredGpIds = new List<Id>();
    for (Contact gp : Trigger.new) {
        if (gp.GPChange__c && gp.NewGP__c != null) {
            retiredGpIds.add(gp.Id);
        }
    }

    if (!retiredGpIds.isEmpty()) {
        ContactTriggerHandler.updateGeneralPracticioner(retiredGpIds);
    }
}

Upvotes: 2

Reverb
Reverb

Reputation: 33

@RubenDG

The script does the job, very nice. But when the are more results >~100 records, I get a APEX CPU time limit. There are also other processes on object account, but not heavy ones. Disabled for the test some other WF rules or processes related to Account, but still get the CPU limit. I think the max updated patients never exceed more then 1500... In other words. No general practitioner has more then 1500 patients. So that isn't a lot.

Screenshot

The weird thing, when i try to use Dataloader or Workbench and do an update(1200 records) on field GeneralPracticioner__c on Account, it works perfect and doesn't have any apex CPU errors.

What would you advice. Write a handler class that is async? Of make the trigger asyc(is that even possible?)

Upvotes: 1

Related Questions