trigger OpportunityUpdateTrigger on Opportunity (after update, before update) {
    for(Opportunity o : trigger.new ){
        for(Opportunity old : trigger.old){

        List<OpportunityLineItem> getOLI  = new List<OpportunityLineItem>();
        List<Product2> getProducts = new List<Product2>();
             List<OpportunityLineItem> getP2Id  = new List<OpportunityLineItem>();
           getProducts = [ SELECT Id, Basis__c, Family FROM Product2 WHERE Id=:getOLI.Product2Id];
            getOLI = [SELECT Id, Product2Id FROM OpportunityLineItem WHERE 

Need to access revenue scheduling from when an opportunity object is updated but getting this error:

Initial term of field expression must be a concrete SObject: List Line item

  • 1
    getOLI is a list, so you can't use a field expression with it. Seems like you cut out some code, because the list would always be empty, so you'd never get any results. Also, the query inside a loop inside a loop is just asking for trouble.
    – sfdcfox
    Nov 30, 2016 at 17:51
  • How should it be laid out if I want to reference both old and new triggers? Relatively new to apex
    – Nebbyyy
    Nov 30, 2016 at 17:56

1 Answer 1


The reason for the specific error is your reference to getOLI.Product2Id, because getOLI is of the type List<OpportunityLineItem>, and that Type has no property named Product2Id. You would need to reference a single record, not a collection.

It's hard to say how to get your code "working" since you don't do anything with the data, but here are a few more things you should fix:

  • Don't perform queries inside your loop structure. Query before and use Map structures to retrieve the data. The basic idea when querying child data is to group by the parent:

    Map<Id, List<ChildObject>> children = new Map<Id, List<ChildObject>>();
    for (ChildObject child : query)
        if (!children.containsKey(child.ParentId))
            children.put(child.ParentId, new List<ChildObject>());
  • Don't use nested loops. Your current code runs the inner loop exponentially more as the number of trigger records grows. With 200 records, it would be run 40,000 times. You can instead simply get the corresponding old record from oldMap without torching your CPU time.

    for (Opportunity newRecord : trigger.new)
        Opportunity oldRecord = trigger.oldMap.get(newRecord.Id);
        // other logic
  • Pick one, before or after. You shouldn't run the same logic on both operations, that just doubles the already horrendous consumption of your governor limits. If you are updating the trigger records, use before. If you are updating related records, use after. See Triggers:

    There are two types of triggers:

    • Before triggers are used to update or validate record values before they’re saved to the database.
    • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records, such as logging into an audit table or firing asynchronous events with a queue. The records that fire the after trigger are read-only.

You must log in to answer this question.

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