0

I'm pretty close to being maxed out on workflows for my custom objects. So I decided to see if apex classes could help, I'm still currently trying to learn how to write triggers and classes so Im pretty lost.

I need to be able to create a related record when a lookup field is changed.

My object, storages has a related list of packages. When a field on storage is change, it should create a new package. Also for later, if the field changes again with the biohazard field = false we need to capture the time/date it happens on a date/time field on that said package record.

I know I can capture some of this with process builder but like I mentioned im very close to maxing out and would like an alternative or something to work off of. I plan on using process builder to delete this records when storage is in a certain pick list option.

EDIT:

my class I have so far for this. It works but it is creating 2 records at a time. Should i not create a list since I only want one record to be created? when i remove the list and just to storage__c storage and remove the first for it gives me an error "caused by: System.NullPointerException: Attempt to de-reference a null object:

public with sharing class newPackagePPP {

 //create package record  when storage field changes 
 public static void createPPP(Map < Id, storage__c > newStorages,
  Map < Id, storage__c > oldStorages, list < storage__c > storages) {
  List < Packages__c > P = new List < Packages__c > ();
  for (Id id: newStorages.keyset()) {
   for (Storage__c storage: storages) {
    if ((newStorages.get(id).BIO__c = TRUE ||
      newStorages.get(id).BioSent__c == 'Confirmed') && .
     (oldStorages.get(id).BIO__c = False ||
      oldStorages.get(id).BioSent__c != 'Confirmed')) {
     P.add(new Packages__c(Storage__c = storage.id, PPP__c ==
      TRUE));
     insert P;
    }
   }
  }
 }
}

Im sure there is a much simpler way to do this but I took a shot at it.

Edit:

  public with sharing class newPackagePPP {

  //create package record  when storage field changes 
  public static void createPPP(
  Map < Id, storage__c > oldStorages,  List<storage__c> storages) {
  list<Packages__c>  P = new list<Packages__c> ();
   for(Storage__c storage :storages)  {
  if ((Storage.BIO__c = TRUE ||
  Storage.BioSent__c == 'Confirmed') && .
 (oldStorages.get(storage.id).BIO__c = False ||
  oldStorages.get(storage.id).BioSent__c != 'Confirmed')) {
  P.add(new package__c(storage__c = storage.id, PPP__c = TRUE));
 insert P;
    }
   }
 }
 }

}

My trigger:

    else if (Trigger.IsAfter && Trigger.isUpdate){
    StorageTriggerHandler.handleAfterUpdate(Trigger.NewMap, 
    Trigger.OldMap);
    }

trigger handler includes this:

    Packages.createPPP(oldStorages,newStorages.values());
  • This is achievable with Apex (and, as you know, Process Builder). What specific assistance are you seeking? Trailhead has some great resources to learn how to write triggers, if you haven't already taken advantage of those modules. – David Reed Apr 21 '18 at 21:00
  • I have a trigger already in place and a handler to help "read" my other methods/classes that a contractor written up before I was hired. Is it better to write this in the trigger handler already in place or write a new class and have the handler read the methods in the class? – J.koh Apr 21 '18 at 22:37
1

Original Version

I see two logic errors here, although I'd think you would get an exception before seeing multiple objects inserted.

You're iterating over the same objects twice:

for (Id id: newStorages.keyset()) {
   for (Storage__c storage: storages) {

I'm assuming this method is called from a trigger's update event as createPPP(Trigger.newMap, Trigger.oldMap, Trigger.new);

In that case, you are iterating over the set of updated Ids, but then within that loop iterating over the list of new records again. There's no need to do that and it'll result in multiplicative insertions.

You can remove the inner loop entirely, since you do not refer to its loop variable storage.

Secondly, you're performing DML in a loop:

 insert P;

This would result in inserting multiple objects if it didn't throw an exception first for trying to insert objects that already have a populated Id value. You should move your insert outside the loop as the final statement executed in the method.

If you're getting NullPointerException, I wonder if you're calling this from both insert and update trigger events. insert events by nature don't have an oldMap.

Revised Version

Taking in some of the material from comments.

Your new version is executing DML for each trigger object and will result in limits exceptions at volume. Since it contains no iteration, it cannot insert two records, meaning the issue lies elsewhere - like this method being called twice in a transaction for the same record.

You mention that your handler calls no fewer than 14 functions for this trigger. My suspicion is that one of the other functions fires a new update DML on the Storage__c object, resulting in trigger recursion and execution of your class a second time on the same objects. To determine whether this is the case, you may want to include System.debug() statements in your handler to see how many times it's called when you update a record.

If you do have trigger recursion, you'll need to ensure that your method only executes once for any given object. A static Set<Id> variable tracking all of the Storage__c records already acted upon in a transaction is one way to approach this, but given the apparent complexity of your trigger architecture I am leery of prescribing an exact approach for you. If you have access to an architect or lead developer on your team, you may wish to work with them to ensure your code and your recursion prevention strategy works effectively with this complex trigger and handler stack.

  • I had removed the iterations last night. i posted the edits, it still is inserting two records. – J.koh Apr 22 '18 at 13:36
  • 1
    Your new version is executing DML for each trigger object and may result in limits exceptions. Since it contains no iteration, it cannot insert two records, meaning the issue lies elsewhere - like this method being called twice in a transaction for the same record. – David Reed Apr 22 '18 at 13:44
  • Should I just scratch it and start over then? I had removed the inner iteration and wasn't able to add the certain fields for packages. – J.koh Apr 22 '18 at 15:01
  • Not necessarily - I thought your first version was pretty close. It's possible there's an issue in the trigger itself, if you're still getting duplicate records with version 2. – David Reed Apr 22 '18 at 15:12
  • So keep the first version but remove the second iteration? but then I wouldnt be able to add the fields for packages. I'll edit the top to include my trigger. – J.koh Apr 22 '18 at 15:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.