1

I'm new to apex and still trying to figure things out. I'm attempting to pull the Parent data from the Child to then update. I keep receiving the error "Variable does not exist: Location__r".

Please not the custom object singular is "Locations" and the field of opportunity is "Location".

public class OpportunityTriggerHandler {

    //Setting list of opportunities IDs
    public static void opptoAcctModifier(Set<ID>oppsIds)
        {
            //Create list of locations to update
            List<Locations__c> modifiedLocs = new List<Locations__c>();
            //Grabbing Opportunity and Related Locations fields
            for(Opportunity opp : [SELECT Id,Location__c,Locations__r.Status__c,Locations__r.Original_Payment_Amount__c,Locations__r.Date_Payment_Processed__c,Locations__r.Date_Payment_Received__c FROM Opportunity WHERE Id IN :oppsIds])
                {
                    //Locations fields to update and Opportunity fields to pull from
                    opp.Locations__r.Original_Payment_Amount__c = opp.Amount;
                    opp.Locations__r.Date_Payment_Processed__c = opp.CloseDate;
                    opp.Locations__r.Date_Payment_Received__c = opp.CloseDate;
                    opp.Locations__r.Status__C = opp.Billing_Status__c;
                    modifiedLocs.add(opp.Location__c);
                }
            System.debug(modifiedLocs);
            //update modifiedLocs;
        }
}
0

There are a few things wrong in your provided code, but it should be pretty easy to fix.

Before we get to that though, some brief explanation.

  • Master-Detail and Lookup relationship fields that end in __c (for custom relationships) or Id (e.g. AccountId a "standard" relationship on the Opportunity object) contain the Id of the related record
  • Where you have a relationship field, you also have a pseudo-field ending in __r for custom relationships and dropping the "Id" (e.g. Account instead of AccountId on Opportunity) for most standard relationships
  • This pseudo-field contains a full-fledged SObject instance of your related record
    • This analogy isn't really an intuitive way to think about how to construct the query, but it is an intuitive way to understand the row data returned by the query
  • Using the plural of the field name is typically only done when your query goes in the opposite direction (i.e. from parent object to the child object)

With that out of the way, let's fix your code

public static void opptoAcctModifier(Set<ID>oppsIds){
    List<Locations__c> modifiedLocs = new List<Locations__c>();

    // When querying for fields on a related, parent record, you just change the __c
    //  to __r
    // The plural isn't used in this situation
    for(Opportunity opp : [SELECT Id, Location__c, Location__r.Status__c, Location__r.Original_Payment_Amount__c, Location__r.Date_Payment_Processed__c, Location__r.Date_Payment_Received__c FROM Opportunity WHERE Id IN :oppsIds]){
        // Similarly, when accessing/modifying related record data, we use __r
        // (still no plural)
        opp.Location__r.Original_Payment_Amount__c = opp.Amount;
        opp.Location__r.Date_Payment_Processed__c = opp.CloseDate;
        opp.Location__r.Date_Payment_Received__c = opp.CloseDate;
        opp.Location__r.Status__c = opp.Billing_Status__c;

        // To perform DML, we need a collection of SObjects (Location__c is an SObject)
        // Your list is also defined as a collection of Location__c rather than Id
        // So you want to use opp.Location__r here (which contains a Location__c record)
        modifiedLocs.add(opp.Location__r);
    }
    System.debug(modifiedLocs);
    update modifiedLocs;
}

Querying for parent fields in order to update them seems a little strange to me though. It would certainly work, but querying for the parent fields is unnecessary.

Given that this is code that appears to be run as part of a trigger, the entire query is not strictly required either. The limit of 100 queries per (synchronous) transaction is the most common one people run into, so anything we can do to reduce queries is worth considering.

If I were to write this method, I'd make use of the trigger context variables (passed in as a List to the method) and our ability to use the SObject constructor to set a record Id (which allows us to update a record without querying for it)


public static void opptoAcctModifier(List<Opportunity> opps){
    List<Locations__c> modifiedLocs = new List<Locations__c>();

    for(Opportunity opp :opps){
        // A quick, simple guard clause to prevent issues if an Opp
        //   is not related to a location
        // By passing in trigger.new to this method, we'll automatically
        //   get access to Location__c without querying.
        // That makes unit testing a bit more painful (you need to remember
        //   to query for or include Location__c in the test data you pass in), but
        //  I'm of the opinion that it's a worthwhile tradeoff
        if(opp.Location__c == null){
            // the continue keyword causes us to skip the rest of the loop
            //   and start processing the next record in the collection
            continue;
        }

        // In the SObject constructor, we can set the Id field (which is 
        //   otherwise read-only).
        // We can also set additional fields (separate each field = value pair
        //   with a comma like you're passing arguments to a method), and this
        //   is the fastest way to set fields on an SObject.
        // That speed doesn't really matter though, the bigger gain is that this
        //   ends up requiring a bit less typing
        modifiedLocs.add(new Location__c(
            Id = opp.Location__c,
            Original_Payment_Amount__c = opp.Amount,
            Date_Payment_Processed__c = opp.CloseDate,
            Date_Payment_Received__c = opp.CloseDate,
            Status__c = opp.Billing_Status__c
        ));
    }
    System.debug(modifiedLocs);
    update modifiedLocs;
}
| improve this answer | |
  • Thank you so much. This was way simpler and straight forward. I greatly appreciate your explanation as well, it left nothing for me to question! – Egypte May 13 at 23:40
0

You'll want to change this line:

modifiedLocs.add(opp.Location__c);

Into

modifiedLocs.add(opp.Location__r);

As Location__c refers just to the lookup field, but Location__r refers to the entity itself. However, per the error message you get, I suspect you might have another issue. Are you sure it's Locations__r and not Location__r? Seems reasonable that the name would be singular and not plural, since this is a parent field.

| improve this answer | |
  • The singular is Locations so it would still be Location__r. Thanks for asking! I'm unsure why it's not recognizing the related object name. – Egypte May 13 at 23:15

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.