I am attempting to set a flag on the Opportunity Header based on an Aggregate Query of the Opportunity Line Item object, and I keep getting an SObject Exception. My code and the error log are below, but at a high level, here is what I am attempting: After Insert, After Update, and Before Delete I am performing an Aggregate Query of all Opty Line Items in the Set of Optys that were affected by the DML operation on Opty Line Item. The Aggregate Query counts up all the line items that have ricebookEntry.Product2.GL_Account_Rollup__c == Software and groups it by Opty Id. If the count is > 0, it should set the flag on the Opty Header to Yes, if the count is 0 it should set it to No. Thanks in advance for your help.

public static trigger_killswitch__c tks = trigger_killswitch__c.getInstance('Baseline Rollup Summary');
public static boolean killswitch = tks.disabled__c;

public static void UpdateSoftwareFulfillmentFlag(List<OpportunityLineItem> optyLineItems){

    //check trigger killswitch
    if (killswitch != true){
        System.debug('Entering UpdateSoftwareFulfillmentFlag method');

            Set<Id> optyIdSet = new Set<Id>();  //set of affected optys
            List<AggregateResult> countOfSoftwareLineItems = new List<AggregateResult>();  //the count of all software line items in the affected optys grouped by opty
            Map<ID, Opportunity> optysForUpdate = new Map<ID, Opportunity>(); //the list of optys
            Opportunity optyForUpdate = new Opportunity();

            //build Set of affected Opty Ids
            for (OpportunityLineItem optyLineItem : optyLineItems){

            //build Aggregate Result List of software lines grouped by opty
            countOfSoftwareLineItems = [SELECT OpportunityId, Count(Id) FROM OpportunityLineItem WHERE OpportunityId IN :optyIdSet AND PricebookEntry.Product2.GL_Account_Rollup__c = 'Software' GROUP BY OpportunityId];

            //loop through the Aggregate Result, set fulfillment flag to no for any Opty with 0 software lines and add to optys for update map, set fulfillment flag to yes for any Opty with >0 software lines and add to optys for update map
            for(AggregateResult ar : countOfSoftwareLineItems){
                if (ar.get('expr0') == 0){
                    optyForUpdate = New Opportunity (Id = (Id)ar.get('OpportunityID'), Fulfillment_Required_For_Products__c = 'No');
                    optyForUpdate.put(optyForUpdate.Id, optyForUpdate);
                    optyForUpdate = New Opportunity (Id = (Id)ar.get('OpportunityID'), Fulfillment_Required_For_Products__c = 'Yes');
                    optyForUpdate.put(optyForUpdate.Id, optyForUpdate);                        

            //update optys
            update optysForUpdate.values();               

        }  //end of try block           

        catch (NullPointerException e){
            ErrorHandler.printError('Null divisor or dividend.', e);

        catch (QueryException e){
            ErrorHandler.printError('A SOQL exception has occurred.', e);

        catch (DMLException e){
            ErrorHandler.printError('A DML Exception has occurred.', e);

        catch (SObjectException e){
            ErrorHandler.printError('An SObject Exception has occurred.', e);

        catch (Exception e){
            ErrorHandler.printError('An unknown exception has occurred.', e);

        finally {
            System.debug('Exiting UpdateSoftwareFulfillmentFlag method');

    }  //end of killswitch brace        

}  //end of UpdateSoftwareFulfillmentFlag method    

enter image description here

  • Wow, I really feel stupid. Thanks to both of you for your help. – Mike Lankfer Jun 11 '15 at 13:50

These lines here are your problem:

optyForUpdate = New Opportunity (Id = (Id)ar.get('OpportunityID'), Fulfillment_Required_For_Products__c = 'No');
optyForUpdate.put(optyForUpdate.Id, optyForUpdate);

optyForUpdate has been declared as an Opportunity type through this: Opportunity optyForUpdate = new Opportunity();

The first line is fine, because you are creating a new opportunity object, and putting it into your optyForUpdate variable. The second line, you are trying to put the value of optyForUpdate (an object), into the field optyForUpdate.Id (which is actually just an Id).

I see you have a map declared called optysForUpdate (note the s), so you may want to change that second line to be optysForUpdate (with the s), and all should work.

On a side note, I would maybe refactor that block to something like this for readability (although some people will disagree of course):

for(AggregateResult ar : countOfSoftwareLineItems){
        new Opportunity(
            Id = (Id)ar.get('OpportunityID'),
            Fulfillment_Required_For_Products__c = (ar.get('expr0') == 0)?'No':'Yes')
  • Sorry, I am really new to Apex (and Object Oriented Programming in general); can you explain what this line is doing? 'Fulfillment_Required_For_Products__c = (ar.get('expr0') == 0)?'No':'Yes')' – Mike Lankfer Jun 11 '15 at 13:52
  • It's called a ternary operator. In raw forms it's (boolean)?true_value:false_value. They can be your friend, and worst enemy. Friend, because they make you life a lot more simple for writing tests (one test scenario vs. two), and enemy because people sometimes put way too much logic into them and they can get over bloated. Just one of those syntactic sugar things. – Ben Naylor Jun 11 '15 at 21:43


optyForUpdate.put(optyForUpdate.Id, optyForUpdate);


optysForUpdate.put(optyForUpdate.Id, optyForUpdate);

The former is trying to call put(field, value) on an Opportunity, and that Id is not the name of any field. The latter is trying to call put(key, value) on a Map<Id, Opportunity> and thus an Opportunity Id is a valid parameter.

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.