+ Start a Discussion
caanguscaangus 

trying to create a map of a child object

Hello,

I am trying to create a map of child objects as they relate to the parent object for a trigger.

I have the standard Contract object, and a custom Time Entry Object. When a Time Entry is created, udpated, or deleted my trigger kicks off and grabs all Time Entries associated with the Contract. It will then calculate the Total Time Entered and the Total Value and update this on the Contract. My current  method works, however I'm running into governor limit exceptions. Best practices states I should use a map instead of multipl SQL calls in a for loop. I've followed some examples online using this method however I am getting the error.

sObject type 'ContractwithTime' is not supported. If you are attempting to use a custom object, be sure to append the '__c' after the entity name. Please reference your WSDL or the describe call for the appropriate names.

Code:
trigger trg_TimeEntryRollupToContract on Time_Entry__c (after delete, after insert, after update) 
{
    double dblTotalHours = 0.0;
    double curTotalValue = 0.00;


    if(Trigger.isInsert||Trigger.isUpdate||Trigger.isDelete)
    { 
        //***********************************************
        //New Record
        //***********************************************
           
        if(Trigger.isInsert)
        { 


           List<Contract> ContractwithTime = [SELECT Id, Total_Time_Entry_Value__c, Total_Time_Entry_Hours__c,
                                                    (SELECT Id, Total_Time__c, Total_Entry_Value__c FROM TimeEntry__r)
                                                    FROM ContractwithTime WHERE Id IN :Trigger.newMap.KeySet()];
            
            for(Contract con : ContractwithTime){
                for(TimeEntry__c ti : ContractwithTime.Time_Entry__r){
                    	if (ti.Total_Time__c == null) ti.Total_Time__c = 0;
                        dblTotalHours += ti.Total_Time__c;
                        if(ti.Time_Entry_Value__c!=null) curTotalValue += ti.Time_Entry_Value__c;
                        if(dblTotalHours > 0) contractwithTime.Total_Time_Entry_Hours__c = dblTotalHours;
;
                        contractwithTime.Total_Time_Entry_Value__c = curTotalValue;
                }
            }
}



 
Jim JamJim Jam
change line 22 ...

for(TimeEntry__c ti : con.Time_Entry__r){
caanguscaangus
Thanks Jim Jam,

While that's most likely another issue i would have ran into it looks like the error message I'm recieving is on the line
 
List<Contract> ContractwithTime = [SELECT Id, Total_Time_Entry_Value__c, Total_Time_Entry_Hours__c,
                                                    (SELECT Id, Total_Time__c, Total_Entry_Value__c FROM TimeEntry__r)
                                                    FROM ContractwithTime WHERE Id IN :Trigger.newMap.KeySet()];

 
Jim JamJim Jam
FROM Contract WHERE Id IN :Trigger.newMap.KeySet()
Jason FlippenJason Flippen
Hi caangus,

I'm seeing three issues...

​First issue is you SOQL query.  Instead of "FROM ContractwithTime" it should be "FROM Contract".

Second issues is what Jim Jam pointed out in his first response.  Change your second "For" loop to this:
 
for (TimeEntry__c ti : con.Time_Entry__r) {

Third issue is inside your second "For" loop you are referencing your Contract List object variable (contractwithTime) instead of the single Contract object variable (con) you are using to iterate through that List.

Try changing the following two lines of code:
 
if (dblTotalHours > 0) contractwithTime.Total_Time_Entry_Hours__c = dblTotalHours;
contractwithTime.Total_Time_Entry_Value__c = curTotalValue;

To this:
 
if (dblTotalHours > 0) con.Total_Time_Entry_Hours__c = dblTotalHours;
con.Total_Time_Entry_Value__c = curTotalValue;

I hope this helps.

Jason
caanguscaangus
Hi Jason,

Thanks for this, I have made the udpates however I'm getting the error

Didn't understand relationship 'TimeEntry__r' in FROM part of query call. If you are attempting to use a custom relationship, be sure to append the '__r' after the custom relationship name. Please reference your WSDL or the describe call for the appropriate names.

on the List<contract> portion of the code. Could be something I'm misunderstanding about the relation between these two objects? A Time Entry has a lookup field to Contract.
 
List<Contract> ContractwithTime = [SELECT Id, Total_Time_Entry_Value__c, Total_Time_Entry_Hours__c,
                                                    (SELECT Id, Total_Time__c, Total_Entry_Value__c FROM TimeEntry__r)
                                                    FROM Contract WHERE Id IN :Trigger.newMap.KeySet()];
            
            for(Contract con : ContractwithTime){
                for(TimeEntry__c ti : con.Time_Entry__r){
                    	if (ti.Total_Time__c == null) ti.Total_Time__c = 0;
                        dblTotalHours += ti.Total_Time__c;
                        if(ti.Time_Entry_Value__c!=null) curTotalValue += ti.Time_Entry_Value__c;
                        if(dblTotalHours > 0) con.Total_Time_Entry_Hours__c = dblTotalHours;

                        //if(curTotalValue > 0) contract.Total_Time_Entry_Value__c = curTotalValue;
                        con.Total_Time_Entry_Value__c = curTotalValue;
                }
            }

 
Jason FlippenJason Flippen

caangus,

I believe the way you've coded your SOQL query, it assumes the Contract object is the one with the lookup to the TimeEntry object.  A way around that is to us the Ids of the Contract records being insert, and create a Map for the related TimeEntry__c objects.  That would look something like this...
// Create a Map connecting TimeEntry records to a Contract (Id).
Map<Id,List<TimeEntry__c>> contractTimeEntryMap = new Map<Id,List<TimeEntry__c>>();
for (TimeEntry__c te : [SELECT Id,
			                   Contract__c,
			                   Total_Time__c,
			                   Total_Entry_Value__c
			            FROM   TimeEntry__c
			            WHERE  Contract__c IN :Trigger.newMap.keySet()) {
	if (!contractTimeEntryMap.containsKey(te.Contract__c)) {
		contractTimeEntryMap.put(te.Contract__c,new List<TimeEntry__c>());
	}
	contractTimeEntryMap.get(te.Contract__c).add(te);
}
This map relates a List of TimeEntry objects to a single Contract record via the Contract Id.  Once you have this Map, you can then iterate through the new Contract records and the related TimeEntry__c records.  That would look something like this...
for (Contract con : [SELECT Id,
			                Total_Time_Entry_Value__c,
			                Total_Time_Entry_Hours__c
		             FROM   Contract
		             WHERE  Id IN :Trigger.newMap.keySet()) {
    
    // Make sure we have TimeEntry records related to this Contract (Id).
	if (contractTimeEntryMap.containsKey(con.Id)) {
        
        // Iterate through the related TimeEntry records.
		for (TimeEntry__c ti : contractTimeEntryMap.get(con.Id).values()) {
            // add your update logic here
		}
	}
}
Keep in mind that if your trigger is on the Contract object you can't just set the values in the TimeEntry__c object and expect them to save.  You'll have to add those records to a List and update that List outside the loop.

I hope this helps.

Jason

P.S.  I didn't test the code I provided so there may be some typo's.