You need to sign in to do that
Don't have an account?
Morgan Marchese
Why Is This Code Throwing a De-Reference Null Object Error?
Hoping someone can help me here, I'm getting a de-reference null object error on some code. This code has been working perfectly up until now so I'm kind of scratching my head on this one, we haven't made any code changes recently to this class. I know that a de-reference means that I am making a call to a null list or variable, and that to combat that you should do checks for .size > 0 before proceeding with any implementation, but, as far as I can tell, we've already done that?
Salesforce reports the error at line 106 which when looking in Salesforce is this line:
That line comes directly after a size check:
So, I'm making it past the .size() > 0 check, but then still de-referencing on the System.debug line directly below it. I'm having trouble tracking down WHAT the null is at this point. Anybody see anything I missed or have any suggestions on tracking the null better?
Thanks for your time.
Salesforce reports the error at line 106 which when looking in Salesforce is this line:
System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id);
That line comes directly after a size check:
if(accToSPCMap.size() > 0){
So, I'm making it past the .size() > 0 check, but then still de-referencing on the System.debug line directly below it. I'm having trouble tracking down WHAT the null is at this point. Anybody see anything I missed or have any suggestions on tracking the null better?
Thanks for your time.
public with sharing class SendAlertForRelatedProductPlans { // Make the constructor private since this is a utility class that should not be instantiated private SendAlertForRelatedProductPlans() { } public static void checkNewSubscriptionProductCharges(List<Zuora__SubscriptionProductCharge__c> newSubscriptionProductCharges) { // The below code will query for the accounts related to the SPCs that are being inserted Set<Id> accountIdSet = new Set<Id>(); List<Messaging.SingleEmailMessage> messages = new List<Messaging.SingleEmailMessage>(); for(Zuora__SubscriptionProductCharge__c spc : newSubscriptionProductCharges) { if(spc.Zuora__Account__c != null && spc.Zuora__Subscription__c != null) // Make sure the SPCs are related to an account and subscription { accountIdSet.add(spc.Zuora__Account__c); } } if(accountIdSet.size() == 0) { System.debug('The SPCs being inserted were not associated with accounts, quitting early.'); return; // If none of the SPCs are related to accounts, quit early } List<Account> accounts = [SELECT Id, Name, Customer_Number__c FROM Account WHERE Id IN :accountIdSet]; // For each account, we'll need to query for *all* the related active subscriptions. Map<Id, Zuora__Subscription__c> subscriptions = new Map<Id, Zuora__Subscription__c>([SELECT Id, Name, Zuora__Account__c FROM Zuora__Subscription__c WHERE Zuora__Account__c IN: accountIdSet AND Zuora__Status__c = 'Active' AND Name != null]); // If there are no active subscriptions associated with the accounts specified, we should not continue. if(subscriptions.size() == 0) { System.debug('There were no active subscriptions associated with the SPCs being inserted. Quitting early.'); return; } Set<Id> subscriptionIdSet = subscriptions.keySet(); // We also need to query for all SPCs associated with the above active subscriptions. // Retrieve any SPCs that are: // (a) Associated with the set of subscriptions ids // (b) The subscriptions must be active // (c) Associated with the set of account ids within this trigger context // (d) The SPC isn't a Discount Record // (e) The SPC has a Rate Plan Classification Map<Id,Zuora__SubscriptionProductCharge__c> spcsAssociatedWithActiveSubscription = new Map<Id,Zuora__SubscriptionProductCharge__c>( [SELECT Id, RatePlanClassification__c, Zuora__Account__c, Zuora__Subscription__c FROM Zuora__SubscriptionProductCharge__c WHERE Name != 'Discount' AND RatePlanClassification__c != null AND Zuora__Subscription__c IN : subscriptionIdSet AND Zuora__Subscription__r.Zuora__Status__c = 'Active' AND Zuora__Account__c IN :accountIdSet]); Map<Id, List<Zuora__SubscriptionProductCharge__c>> accToSPCMap = new Map<Id, List<Zuora__SubscriptionProductCharge__c>> (); for(Zuora__SubscriptionProductCharge__c zs: spcsAssociatedWithActiveSubscription.values()){ List<Zuora__SubscriptionProductCharge__c> sub = accToSPCMap.get(zs.Zuora__Account__c); if(sub == null){ sub = new List<Zuora__SubscriptionProductCharge__c>(); sub.add(zs); accToSPCMap.put(zs.Zuora__Account__c, sub); }else{ sub.add(zs); } } // The below two maps will capture whether a subscription has: // (a) at least one 'Support' related SPC // (b) at least one 'Software' related SPC Integer count = 0; System.debug('KLE accounts : '+accounts.size()); for(Account account : accounts){ Set<ID> subscriptionIdHasSupportSPCs = new Set<ID>(); Set<ID> subscriptionIdHasSoftwareSPCs = new Set<ID>(); Set<ID> subscriptionIdHasCommSPCs = new Set<ID>(); if(accToSPCMap.size() > 0){ System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id); for(Zuora__SubscriptionProductCharge__c spc : accToSPCMap.get(account.id)) { if(spc.RatePlanClassification__c == 'Support') { subscriptionIdHasSupportSPCs.add(spc.Zuora__Subscription__c); } else if(spc.RatePlanClassification__c == 'Software') { subscriptionIdHasSoftwareSPCs.add(spc.Zuora__Subscription__c); } else if(spc.RatePlanClassification__c == 'Communication') { subscriptionIdHasCommSPCs.add(spc.Zuora__Subscription__c); } count++; } // End of loop through SPCs }
Thanks for replying again. After this post and my post on StackExchange, we were able to pinpoint (as you said loosely) that our for loop was iterating through a larger list of accounts than the list we had in the map after all of our filter criteria were applied. The for loop wasn't focusing on only accounts in the map, it was doing a broader list of accounts based on the select statement at line 28.
Once I came to that realization, I figured that we ultimately don't care about everything in the account list, we only care about the things that made their way into the map, so I made the following change:
All Answers
system.debug(accToSPCMap.containsKey(account.id));
If it prints false then that may be your problem as the get method returns null because the key/value pair doesn't exist in the map. You nest another if statement inside to check for that as well.
if(accToSPCMap.containsKey(account.id)){
System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id);
}
Hey Guys,
Many thanks for your answers. I took Terence's suggestion of adding system.debug(accToSPCMap.containsKey(account.id)); after my IF statement, and found that while running through the newly inserted records I get a lot of responses of containsKey: True (which is expected), until finally reaching a containsKey: false which at that point throws the null reference error.
I could use a little more help here if you would be kind enough to provide me with some next troubleshooting steps. It's hard to test this with a single record since these records are inserted automatically via API integration.
From my understanding of this code, one of the first things we do is check if the SPC records being inserted are linked to SFDC Accounts. IF they are tied to accounts, we add them to the accountIdSet and then we check if accountIdSet is > 0 before we continue. If the record being inserted is not tied to an account, shouldn't it not be making it through the checks since there would be no account ID and therefore they wouldn't be put in the list?
I guess my main question is how am I ending up with records in my map that don't have the account.id key? Every record that lands in the map should have an associated account id.
As always, your time and expertise are appreciated!
Thanks for replying again. After this post and my post on StackExchange, we were able to pinpoint (as you said loosely) that our for loop was iterating through a larger list of accounts than the list we had in the map after all of our filter criteria were applied. The for loop wasn't focusing on only accounts in the map, it was doing a broader list of accounts based on the select statement at line 28.
Once I came to that realization, I figured that we ultimately don't care about everything in the account list, we only care about the things that made their way into the map, so I made the following change: