[SalesForce] Before Insert: Using __r to pull related data in a Trigger.New for loop results in nulls even though lookup relationship has Id

Situation: I am trying to build a custom validation rule via apex that will stop users from saving a charge item to a quote if the
charge item exceeds the available product quantity. The charge item
being saved is a child of (and has a lookup relationship to) a "Rate
Plan", which is the object that carries the available product quantity
field.

Object that triggers apex: zqu__QuoteCharge__c

Relationship field on QuoteCharge: zqu__ProductRatePlanCharge__c


The trigger is currently in the BEFORE INSERT context, to block users from saving new records. I pass Trigger.new as a parameter to my class/method: z_QuoteChargeTriggerHandler.customValidation(Trigger.new);


In my for() loop, I've added a bunch of System debugs since my if statement wasn't being triggered.

When I System.debug(zqu__ProductRatePlanCharge__c) it returns an Id, so I know the relationship is there.

However, when I System.debug(zqu__ProductRatePlanCharge__r.Ascent_Quantity__c) I return null. Expected: 0.

Ultimate Question: Since I cannot SOQL query this record during BEFORE INSERT (At least, I think so, since the record hasn't been
inserted yet?) How can I reference related objects using the __r
relationship before insert of a new record in the context of
Trigger.new?

Code:

public void customValidation(List<zqu__QuoteCharge__c> newList){
    if (newList != null && newList.size() > 0){
    List<zqu__ProductRatePlanCharge__c> rpc = new List<zqu__ProductRatePlanCharge__c>(); // create a list per other Stack suggestion
        for(zqu__QuoteCharge__c quoteCharge: newList){
            if(quoteCharge.Name != 'Discount'){
                rpc.add(quoteCharge.zqu__ProductRatePlanCharge__r); // add to that list?
                System.debug('MM++++ rpc: ' +rpc); // rpc returns null but shouldnt?
                System.debug('MM++++ Charge Name: ' +quoteCharge.Name);
                System.debug('MM++++ Charge Quantity: ' +quoteCharge.zqu__Quantity__c);
                System.debug('MM++++ RatePlan Link: ' +quoteCharge.zqu__ProductRatePlanCharge__c); // returns ID (Lookup, expected)
                System.debug('MM++++ RatePlan Name: ' +quoteCharge.zqu__ProductRatePlanCharge__r.Name); // returns null (expect name)
                System.debug('MM++++ Ascent Quantity: ' +quoteCharge.zqu__ProductRatePlanCharge__r.Ascent_Quantity__c); // returns null (expect: 0)

                if(quoteCharge.zqu__ProductRatePlanCharge__r.Ascent_Quantity__c == 0 || quoteCharge.zqu__ProductRatePlanCharge__r.Ascent_Quantity__c < quoteCharge.zqu__Quantity__c){
                    quoteCharge.addError('The Product: ' +quoteCharge.Name + ' only has ' +quoteCharge.zqu__ProductRatePlanCharge__r.Ascent_Quantity__c + ' in stock but you are trying to sell ' +quoteCharge.zqu__Quantity__c);
                }
            }
        }        
    }
}

Best Answer

You need to query for the parent records in order to get this information.

Set<Id> parentIds = new Set<Id>();
for (Child__c newRecord : trigger.new)
    parentIds.add(newRecord.Parent__c);
Map<Id, Parent__c> parents = new Map<Id, Parent__c>([
    SELECT Name FROM Parent__c WHERE Id IN :parentids
]);

Now you can loop through newList and figure out the corresponding parent record.

for(zqu__QuoteCharge__c quoteCharge: newList)
{
    zqu__ProductRatePlanCharge__c parent =
        parents.get(quoteCharge.zqu__ProductRatePlanCharge__c);
}

You actually have an issue with your logic that the above will not fix. If you insert sibling records with the same parent, their sum might be greater than the allowed maximum. You also need to factor in siblings already in the database. It should be something more like:

public static void validateMaximum(List<Child__c> newRecords)
{
    Map<Id, Decimal> parentToQuantity = new Map<Id, Decimal>();
    for (Child__c newRecord : newRecords)
    {
        if (!parentToQuantity.containsKey(newRecord.Parent__c))
            parentToQuantity.put(newRecord.Parent__c, 0);
        parentToQuantity.get(newRecord.Parent__c) += newRecord.Quantity__c;
    }
    // now you have the totals from the trigger context

    Map<Id, Parent__c> parents = new Map<Id, Parent__c>([
        SELECT Maximum__c, (SELECT Quantity__c FROM Children__r)
        WHERE Id IN :parentToQuantity.keySet()
    ];
    for (Parent__c parent : parents.values())
        for (Child__c child : parent.Children__r)
            parentToQuantity.get(parent.Id) += child.Quantity__c;
    // now you have a comprehensive total including existing values

    for (Child__c newRecord : newRecords)
    {
        Parent__c parent = parents.get(newRecord.Parent__c);
        if (parentToQuantity.get(parent.Id) > parent.Maximum__c)
            newRecord.addError('message');
    }
}
Related Topic