+ Start a Discussion
Morgan MarcheseMorgan Marchese 

Added Custom Setting control (using Booleans) to a trigger, now throwing attempt to de-reference null object - whats wrong w/ this code?

Hi All - We have a custom object, called "Subscription Product & Charge", and we have a trigger on it that executes many different apex classes, called "SubscriptionProductChargeTrigger". Sometimes bulk inserts by a third party cause CPU limits on some old bulky code, and I usually end up editing the trigger in sandbox, commenting out the line that causes the limit issue, deploying to prod, running the bulk insert, and then editing the trigger again in sandbox to remove the comment and deploy to prod again. This is of course time consuming.

So, just this weekend I added a Custom Setting (List) called "MSI_TriggerControl" and created 7 checkbox fields with the intention of being able to "De-activate" the trigger by setting them to true, without doing what I just did above ^

I added to the trigger to get the instance of 'TriggerControl' from my MSI_TriggerControl Custom Setting under the alias 'settings', and then directly following that I attempt to create and set 7 distinct Boolean values using the respective true/false value of the corresponding checkbox in MSI_TriggerControl__c('TriggerControl'). This works perfectly in dev and properly disables sections of the trigger during all testing, but during deployment it throws a de-reference null exception on line 6 which is the first Boolean I am trying to set (Disable_AlertForRelatedSPC). Yes the 'TriggerControl' record is created and yes I deployed all 7 checkbox fields w/ the same API names to Production already... All I can say is that every time one of my test classes attempts to insert an SPC I get a de-reference on line 6 of this trigger.

I modified the trigger using some help and resources from: http://www.sfdc99.com/2014/03/02/change-your-code-on-the-fly-using-custom-settings/

Here is my new code:
trigger SubscriptionProductChargeTrigger on Zuora__SubscriptionProductCharge__c (after insert, after update)
{

// Grab Custom Setting values
    MSI_TriggerControl__c settings = MSI_TriggerControl__c.getInstance('TriggerControl');
    Boolean Disable_AlertForRelatedSPC = settings.Disable_SPCTrigger_AlertForRelatedSPC__c;
    Boolean Disable_SetAnniversaryDate = settings.Disable_SPCTrigger_SetAnniversaryDate__c;   
    Boolean Disable_UpdateSupportLevel = settings.Disable_SPCTrigger_UpdateSupportLevel__c;   
    Boolean Disable_InsertCasesUsingSPC = settings.Disable_SPCTrigger_InsertCasesUsingSPC__c;  
    Boolean Disable_UpdateTrainingMinutes = settings.Disable_SPCTrigger_UpdateTrainingMinutes__c;    
    Boolean Disable_UpdateAccountRecord = settings.Disable_SPCTrigger_UpdateAccountRecord__c;  
    Boolean Disable_CreateUpdateSPC = settings.Disable_SPCTrigger_CreateUpdateSPC__c;
    
    // ********************
    // *** AFTER INSERT ***
    //*********************

    /*
    if(TriggerControl__c.getValues('runSubscriptionProdChargeTrigger') != null)
        return;
    */
    if(trigger.isAfter && trigger.isInsert){

        //Account update map moved here so Accounts will be updated only once
        Map<ID, Account> accountsMap = new Map<ID, Account>();
        if(Disable_AlertForRelatedSPC == false){
        SendAlertForRelatedProductPlans.checkNewSubscriptionProductCharges(trigger.new); // No objects are modified
        }
        if(Disable_SetAnniversaryDate == false){
        AccountUpdateHandler.updateAnniversaryDateUsingSPCList(trigger.new, accountsMap); // This updates accounts
        }
        if(Disable_UpdateSupportLevel == false){
        AccountUpdateHandler.updateSupportLevelUsingSPCList(trigger.new, accountsMap); // This updates accounts
        }
        if(!ZuoraUtilityClass.isZuoraSyncRunning()){
            // Note: The below two method calls should not be separated.  If we insert cases, we must update accounts as well
            if(Disable_InsertCasesUsingSPC == false){
            CaseHandler.insertCasesUsingSPCList(trigger.new); // This inserts cases 
            }
           // AccountUpdateHandler.updateInstallTypeUsingSPCList(trigger.new, accountsMap); // This updates accounts
        }
        if(Disable_UpdateTrainingMinutes == false){
        AccountUpdateHandler.updateTotalTrainingMinutes(trigger.new, accountsMap); // This updates accounts        
        }
        if(Disable_UpdateAccountRecord == false){
        AccountUpdateFromSPC.updateAccountRecord(trigger.New, accountsMap); //Updates Billing Status, Active and Platform Application fields on the Account
        }
        try{
            //one account update instead of many
            update accountsMap.values();
        }
        catch(DmlException e){
            System.debug('The following exception has occurred: ' + e.getMessage());

        } 

        if(Disable_CreateUpdateSPC == false){
        CRAHandlerUsingSPCClass.CreateUpdateCRARecord(trigger.New); //This creates/updates CRA (Account Administration Record)
        }
    }
}
Help me Salesforce Developer forum, you're my only hope!
 
Best Answer chosen by Morgan Marchese
James LoghryJames Loghry
Hi again,

I missed the part about deploying / testing.  The reason your getting a NPE is by design.  There is no data when you run a unit test, minus a few standard records like Users, standard pricebooks, and community related records.  Instead, any time you insert or update a Zuora_SubscriptionProductChange__c record in a unit test, you will also need to insert your custom setting record(s) first.

To do this, I suggest you create a helper method that inserts this custom setting, and then call it any associated unit test.

All Answers

James LoghryJames Loghry
Nice Star Wars reference at the end there ;-)

Your code is throwing a Null Pointer Exception (I strongly suspect) because there is no custom setting record with the Name "TriggerControl" in that particular salesforce org.

It's likely you created the custom setting record in your dev environment, but then when you deploy, it copies over the meta data (custom object and fields), but no data or records.

If you create the TriggerControl record in your "deployment" or production? environment, then it should work.

If not, try switching "getInstance" to "getValues" in line 5 and see if that does anything (it shouldn't, but who knows).

Also, why the 7 different check boxes for your custom setting?  Instead, you could get by with 1 standard Name field, and 1 custom checkbox "Value" field, but with 7 different custom setting records.  That way, you could easily add additional checks in the future, if needed.

Hope that helps.
Morgan MarcheseMorgan Marchese
Hi James!

Glad you read far enough to see my star wars reference!

To answer your question... I did already check both things that you suggested and the results appear to be the same :( - I confirmed that 'TriggerControl' does exist within the MSI_TriggerControl__c custom settings object in my production org, it originally didn't and it caused 7 failures on line 6 but after adding it I only get 4 errors but still on line 6 so I don't really know how to explain that weirdness but the TriggerControl record is definitely there.

I also tried changing to getValues assuming that it might work better since getInstance is more heirarchy-based, but alas, same result... I'm stumped. Any other suggestions come to mind knowing the above?

Regarding your comment about 7 different checkboxes for the custom setting - good point, I hadn't really thought of that.. Custom Settings are brand new to me, this will be my first time using them in this context. I assume you mean that within MSI_TriggerControl__c I could create just 1 checkbox called "Disable Trigger" and then create 7 different records, one for each trigger portion I want to disable? And then in the code I would use getValues or getInstance 7 different times for each of my records and assign 7 different booleans? 
Morgan MarcheseMorgan Marchese
I also used the dev console / query editor to query the object and was able to return results in Production to confirm that the TriggerControl record shows in the DB with all of the default false values.

User-added image

User-added image
James LoghryJames Loghry
Hi again,

I missed the part about deploying / testing.  The reason your getting a NPE is by design.  There is no data when you run a unit test, minus a few standard records like Users, standard pricebooks, and community related records.  Instead, any time you insert or update a Zuora_SubscriptionProductChange__c record in a unit test, you will also need to insert your custom setting record(s) first.

To do this, I suggest you create a helper method that inserts this custom setting, and then call it any associated unit test.
This was selected as the best answer
Abhishek BansalAbhishek Bansal
Hi Morgan,

Yes, James is absolutely right here.
Your test class is not founding any custom setting record and hence returning the null pointer exception.
So you have to create the custom setting record with same name in your test class.
This will definitely solve your problem.

Please let us know if we can provide you more help or information on this.

Thanks,
Abhishek bansal.
Morgan MarcheseMorgan Marchese
Abhishek, thank you for confirming James' thoughts on this.

I took James' advise and went with a single checkbox field called "Disable" and used 7 different Custom Settings records within my TriggerControl List Custom Setting, modified my trigger to accept these new records instead of 1 record w/ 7 fields, and then I created a Helper Class that creates all 7 settings records and inserts the list, which I then referenced in all effected test classes.

Now my solution is ready for the future, with the ease of adding new records without needing to modify all of my test classes. Thanks so much guys! Happy holidays to you both and anyone else reading this!