function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
sliptonslipton 

Deployment Error - MISSING_ARGUMENT, Id not specified in an update call:

I'm brand new to triggers and apex so I'm tending to borrow code snippets from blogs and websites and trying to appropriate them for my org.  I found two classes that help me easily add lookup roll up summary fields to objects.  The classes are listed here:  https://github.com/abhinavguptas/Salesforce-Lookup-Rollup-Summaries/tree/master/classes

 

I then used the example on that site to create a lookup roll up summary field trigger in my sandbox which worked perfectly.  The problem is I'm unable to deploy it to my production environment as I keep receiving 17 or so instances of the below error:

 

"Failure Message: "System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, OppRollup: execution of AfterInsert caused by: System.DmlException: Update failed. First exception on row 0; first error: MISSING_ARGUMENT, Id not specified in an update call: [] Trigger.OppRollup:..."

 

The OppRollup Trigger is as follows:

 

"trigger OppRollup on Opportunity (after insert, after update, 
                                        after delete, after undelete) {
      // modified objects whose parent records should be updated
     Opportunity[] objects = null;   

     if (Trigger.isDelete) {
         objects = Trigger.old;
     } else {
        /*
            Handle any filtering required, specially on Trigger.isUpdate event. If the rolled up fields
            are not changed, then please make sure you skip the rollup operation.
            We are not adding that for sake of similicity of this illustration.
        */
        objects = Trigger.new;
     }

     /*
      First step is to create a context for LREngine, by specifying parent and child objects and
      lookup relationship field name
     */
     LREngine.Context ctx = new LREngine.Context(Contact.SobjectType, // parent object
                                            Opportunity.SobjectType,  // child object
                                            Schema.SObjectType.Opportunity.fields.key_broker_contact__c // relationship field name
                                            );     
     /*
      Next, one can add multiple rollup fields on the above relationship. 
      Here specify 
       1. The field to aggregate in child object
       2. The field to which aggregated value will be saved in master/parent object
       3. The aggregate operation to be done i.e. SUM, AVG, COUNT, MIN/MAX
     */
     ctx.add(
            new LREngine.RollupSummaryField(
                                            Schema.SObjectType.Contact.fields.OppCount__c,
                                            Schema.SObjectType.Opportunity.fields.Amount,
                                            LREngine.RollupOperation.Count 
                                         ));                                       

     /* 
      Calling rollup method returns in memory master objects with aggregated values in them. 
      Please note these master records are not persisted back, so that client gets a chance 
      to post process them after rollup
      */ 
     Sobject[] masters = LREngine.rollUp(ctx, objects);    

     // Persiste the changes in master
     update masters;"

 

As I said I'm brand new to Apex although I do have some education in Java and C++.  Any ideas?  I think I have to put something in the first ELSE statement although I've no idea where to start.  Hopefully a solution here will help me fix many of my deployment issues as I've seen this error message a lot.

 

Thanks in advance!

Best Answer chosen by Admin (Salesforce Developers) 
Sean TanSean Tan

Sorry looks like I lead you into a hole you didn't need to go. Based off the code it just takes the records with the Master Record Id field, if that happens to be null (in your lookup) than that method will end up giving you a SObject without an Id resulting in the update call to fail.

 

Try this for the trigger:

 

trigger OppRollup on Opportunity (after insert, after update, 
after delete, after undelete) {
	// modified objects whose parent records should be updated
	Opportunity[] objects = new Opportunity[]{};   
	
	for (Opportunity o : Trigger.isDelete ? Trigger.old : Trigger.new)
	{
		if (o.key_broker_contact__c != null)
		{
			objects.add(o);
		}
	}
	
	/*
		First step is to create a context for LREngine, by specifying parent and child objects and
		lookup relationship field name
	*/
	LREngine.Context ctx = new LREngine.Context(Contact.SobjectType, // parent object
	Opportunity.SobjectType,  // child object
	Schema.SObjectType.Opportunity.fields.key_broker_contact__c // relationship field name
	);     
	/*
		Next, one can add multiple rollup fields on the above relationship. 
		Here specify 
		1. The field to aggregate in child object
		2. The field to which aggregated value will be saved in master/parent object
		3. The aggregate operation to be done i.e. SUM, AVG, COUNT, MIN/MAX
	*/
	ctx.add(
	new LREngine.RollupSummaryField(
	Schema.SObjectType.Contact.fields.OppCount__c,
	Schema.SObjectType.Opportunity.fields.Amount,
	LREngine.RollupOperation.Count 
	));                                       
	
	/* 
		Calling rollup method returns in memory master objects with aggregated values in them. 
		Please note these master records are not persisted back, so that client gets a chance 
		to post process them after rollup
	*/ 
	Sobject[] masters = LREngine.rollUp(ctx, objects);    
	
	// Persiste the changes in master
	update masters;

 

All Answers

hitesh90hitesh90

Hi slipton,

 

In above trigger you have to put one if condition as like below. and then make deployment

 

if(masters.size() > 0){
   update masters;
}

 

Important :

 

Hit Kudos if this provides you with useful information and if this is what you where looking for then please mark it as a solution for other benefits.

 

Thanks,

Hitesh Patel

sliptonslipton

Hi Hitesh90,

 

That didn't work unfortunately.  Still getting the same 17 errors.

Sean TanSean Tan

I would take a look at this method:

 

Sobject[] masters = LREngine.rollUp(ctx, objects);   

 

Specifically see if anything happening in that particular method is creating a new record and not updating an existing record. If so you'll either want to trap those cases or change your update call to an upsert call.

sliptonslipton
The rollUp method looks like this:

/**
Key driver method that rolls up lookup fields based on the context. This is specially useful in Trigger context.

@param ctx Context the complete context required to rollup
@param detailRecordsFromTrigger child/detail records which are modified/created/deleted during the Trigger
@returns Array of in memory master objects. These objects are not updated back to the database
because we want client or calling code to have this freedom to do some post processing and update when required.
*/
public static Sobject[] rollUp(Context ctx, Sobject[] detailRecordsFromTrigger) {

// API name of the lookup field on detail sobject
String lookUpFieldName = ctx.lookupField.getName();

Set<Id> masterRecordIds = new Set<Id>();
for (Sobject kid : detailRecordsFromTrigger) {
masterRecordIds.add((Id)kid.get(lookUpFieldName));
}
return rollUp(ctx, masterRecordIds);
}
Sean TanSean Tan

You'll need to go a step further and look at this:

 

return rollUp(ctx, masterRecordIds);

 

Which is an overload of the previous method but it seems to take the master record Id's instead of the objects being processed in the trigger.

sliptonslipton

OK.  I'm a bit lost with this one now.

 

public static Sobject[] rollUp(Context ctx,  Set<Id> masterIds) {
        // K: Id of master record
        // V: Empty sobject with ID field, this will be used for updating the masters
        Map<Id, Sobject> masterRecordsMap = new Map<Id, Sobject>(); 
        for (Id mId : masterIds) {
            masterRecordsMap.put(mId, ctx.master.newSobject(mId));
        }

        // #0 token : SOQL projection
        String soqlProjection = ctx.lookupField.getName();
        // k: detail field name, v: master field name
        Map<String, String> detail2MasterFldMap = new Map<String, String>();
        for (RollupSummaryField rsf : ctx.fieldsToRoll) {
            // create aggreate projection with alias for easy fetching via AggregateResult class
            // i.e. SUM(Amount) Amount
            soqlProjection += ', ' + rsf.operation + '(' + rsf.detail.getName() + ') ' + rsf.detail.getName();
            detail2MasterFldMap.put(rsf.detail.getName(), rsf.master.getName());
        }

        // #1 token for SOQL_TEMPLATE
        String detailTblName = ctx.detail.getDescribe().getName();
        
        // #2 Where clause
        String whereClause = '';
        if (ctx.detailWhereClause != null && ctx.detailWhereClause.trim().length() > 0) {
            whereClause = 'WHERE ' + ctx.detailWhereClause ;
        }
        
        // #3 Group by field
        String grpByFld = ctx.lookupField.getName();
        
        String soql = String.format(SOQL_TEMPLATE, new String[]{soqlProjection, detailTblName, whereClause, grpByFld});
        // aggregated results 
        List<AggregateResult> results = Database.query(soql);
        
        for (AggregateResult res : results){
            Id masterRecId = (Id)res.get(grpByFld);
            Sobject masterObj = masterRecordsMap.get(masterRecId);
            if (masterObj == null) {
                System.debug(Logginglevel.WARN, 'No master record found for ID :' + masterRecId);
                continue;
            }

            for (String detailFld : detail2MasterFldMap.keySet()) {
                Object aggregatedDetailVal = res.get(detailFld);
                masterObj.put(detail2MasterFldMap.get(detailFld), aggregatedDetailVal);
            }           
        }
        
        return masterRecordsMap.values();   
    }

Sean TanSean Tan

Sorry looks like I lead you into a hole you didn't need to go. Based off the code it just takes the records with the Master Record Id field, if that happens to be null (in your lookup) than that method will end up giving you a SObject without an Id resulting in the update call to fail.

 

Try this for the trigger:

 

trigger OppRollup on Opportunity (after insert, after update, 
after delete, after undelete) {
	// modified objects whose parent records should be updated
	Opportunity[] objects = new Opportunity[]{};   
	
	for (Opportunity o : Trigger.isDelete ? Trigger.old : Trigger.new)
	{
		if (o.key_broker_contact__c != null)
		{
			objects.add(o);
		}
	}
	
	/*
		First step is to create a context for LREngine, by specifying parent and child objects and
		lookup relationship field name
	*/
	LREngine.Context ctx = new LREngine.Context(Contact.SobjectType, // parent object
	Opportunity.SobjectType,  // child object
	Schema.SObjectType.Opportunity.fields.key_broker_contact__c // relationship field name
	);     
	/*
		Next, one can add multiple rollup fields on the above relationship. 
		Here specify 
		1. The field to aggregate in child object
		2. The field to which aggregated value will be saved in master/parent object
		3. The aggregate operation to be done i.e. SUM, AVG, COUNT, MIN/MAX
	*/
	ctx.add(
	new LREngine.RollupSummaryField(
	Schema.SObjectType.Contact.fields.OppCount__c,
	Schema.SObjectType.Opportunity.fields.Amount,
	LREngine.RollupOperation.Count 
	));                                       
	
	/* 
		Calling rollup method returns in memory master objects with aggregated values in them. 
		Please note these master records are not persisted back, so that client gets a chance 
		to post process them after rollup
	*/ 
	Sobject[] masters = LREngine.rollUp(ctx, objects);    
	
	// Persiste the changes in master
	update masters;

 

This was selected as the best answer
sliptonslipton

I'm just getting an error at line 4 - Error: Compile Error: expecting a semi-colon, found '[' at line 4 column 40.

 

Opportunity[] objects = Opportunity[]{};

 

Does it need a "new" after the equals sign?

Sean TanSean Tan

Yes sorry, I don't get a chance to test compile since I write a lot of this on the fly.

sliptonslipton
All good. Errors help me learn. :)

It seems that fixed it. Thank you so much!
ManzManz
Hello @ Sean Tan,
I am getting below error when ever I am trying to use the below syntax
"LREngine.Context ctx = new LREngine.Context(Parent_API.SobjectType, // parent object
                                                                               Child_API.SobjectType,  // child object
                                                                               Schema.SObjectType.Child.fields.Relationship_Field_API // relationship field name
    );"

Error: Compile Error: Invalid type: LREngine.Context at line 18 column 32

Can you please help me fix this?
I tried in many blogs but I didnt receive any reply from others.

Thanks,
MR