3

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);
                }
            }
        }        
    }
}
9
  • 1
    Trigger contains only object fields. If you want to access parent field. You should query it. Select account.name from contact where Id in trigger new Commented Apr 27, 2016 at 15:39
  • 1
    @AdrianLarson I believe this to not be a duplicate because, while the posts are very similar, mine deals specifically with the BEFORE INSERT context, and the answer to your linked question specifically notes (Quote) "Note this won't work in a before insert as there are no id values in trigger.new". Question still remains, how do I traverse relationships in a before insert scenario? Commented Apr 27, 2016 at 15:45
  • 2
    @MorganMarch I've linked a question with an answer that directly deals with your inquiry. Please let us know if you need further help.
    – sfdcfox
    Commented Apr 27, 2016 at 15:46
  • 1
    @MorganMarch As shown elsewhere, zqu__ProductRatePlanCharge__r is null, and we expect this: you need to loop through Trigger.new, put the ID values from zqu__ProductRatePlanCharge__c into a collection of some sort, and query the parent records directly. Something like Map<Id, zqu__ProductRatePlanCharge__c> charges = new Map<Id, zqu__ProductRatePlanChange__c>(); for(zqu__QuoteCharge__c quoteCharge: newList) { charges.put(quoteCharge.zqu__ProductRatePlanChange__c, null); } charges.putAll([SELECT ... FROM zqu__ProductRatePlanChange__c WHERE Id IN :charges.keySet()]); ... See linked q's.
    – sfdcfox
    Commented Apr 27, 2016 at 16:23
  • 1
    @sfdcfox sorry, I was looking at the selected best answer rather than looking farther at the answers below it. I see now which answer you were referring to in your linked q. I will attempt this. Thanks and sorry for the dupe! Commented Apr 27, 2016 at 16:26

1 Answer 1

1

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');
    }
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .