+ Start a Discussion
DBManagerDBManager 

Set Cloned Record's Master-Detail Parent Based on Query

I want to clone a record, and change the master-detail parent of the cloned record before inserting it.

 

An example:

  • A and B are Item records. 
  • X and Y are Event/Issue records.
  • The Event/Issue object is a master-detail parent of the Item object.
  • X is a master-detail parent of A.
  1. Clone A, creating record B.
  2. From the date field of record X, find the Event/Issue in the next month, Y.
  3. Set the master-detail parent of record B to Y.
  4. Insert B

What I cannot seem to achieve is the query and process for step 2. I have proved steps 3 and 4 can work, by using the Id of a dummy record.

 

Here is my code so far:

trigger SeriesItems on Item__c (after insert) {

for (integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE){
        for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++){
            Item__c copy = Trigger.new[i].clone(false);
            copy.Cloned__c = TRUE;
            copy.Event_Issue__c = 'a08W0000000OiVl'; insert copy; } } } }

 

Hope someone can help.

Best Answer chosen by Admin (Salesforce Developers) 
DBManagerDBManager

Fantastic - after a very small correction (ei.Date__c.monthsBetween(d)==0), your date calculation seems to be right. I checked this by printing the date, d, in a field in the clone, and it is indeed one month on.

 

However, the trigger as a whole did not seem to work. By eliminating one line at a time, I have found that this line was not working:

&& ei.Product__c == copy.Event_Issue__r.Product__c

Presumably this was to do with the fact that the copy record is virtual at this stage.

 

Therefore, I setup a string variable to get the product from the triggering record and used it thus:

trigger SeriesItems on Item__c (after insert) {

//create a Map of records so that you can access records easily based on the Key i.e. Id
Map<Id,Issue_Year__c> eiList = new Map<Id,Issue_Year__c>([SELECT Id, Product__c, Date__c FROM Issue_Year__c ORDER BY Product__c, Date__c]);

List<Item__c> itemList = new List<Item__c>();

for (integer i = 0; i < Trigger.new.size(); i++)
{
    if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE)
    {
        for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++)
        {
            Item__c copy = Trigger.new[i].clone(false);
            copy.Cloned__c = TRUE;
            //Match dates and not just months, they may be of same months but different years
            //Fetch the original Event Master record and start with the month of that record, this was missing
            Date d = eiList.get(Trigger.new[i].Event_Issue__c).Date__c.addMonths(1 + n);
            String p = eiList.get(Trigger.new[i].Event_Issue__c).Product__c;
            for (Issue_Year__c ei :eiList.values())
            {
                if (ei.Id != copy.Event_Issue__c 
                && ei.Product__c == p 
                && ei.Date__c.monthsBetween(d)==0)
                {
                    copy.Event_Issue__c = ei.Id;                                
                }
            }
            itemList.add(copy);
       }
   }
}

if (itemList.size() > 0){
    insert itemList;
    for (Item__c item :itemList){
        item.Line_Number__c = item.Line_Number__c + 1;
    }
    update itemList;
}

}

 

This works, but I will test further and set this as the solution if appropriate.

 

Thanks again, GreatG

All Answers

MiddhaMiddha

Firstly, as per the best practice, do not make insert calls from within the for loop. You should bulkify your trigger. 

 

At the place where you are assigning a dummy Id, write the logic to search for the new Event which has to be assigned to the cloned Item and assign its ID over there. In MD relationship, once you have assigned a parent you cannot change it later on.

DBManagerDBManager

Thanks GreatG.

 

For your first point - how would I go about doing that? Could I add the values of 'copy' to a list, then insert the list outside of the loop, like this:

trigger SeriesItems on Item__c (after insert) {

List<Item__c> itemList = new List<Item__c>();

for (integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE){
        for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++){
            Item__c copy = Trigger.new[i].clone(false);
            copy.Cloned__c = TRUE;
            copy.Event_Issue__c = 'a08W0000000OiVl';
            itemList.add(copy);
        }
    }
}

insert itemList;

}

 

As for your second point, wouldn't that involve an SOQL query inside the for loop, which, again, is not best practice?

 

Thanks again.

MiddhaMiddha

Correct, You can add all the records in a list and outside the for loop, you can insert all the records. Also, you dont need to query records within the for loop as you can do that before you even start the loop.

 

You already have the information that you need to query records before the for loop hence query all the records and put them in a collection(Map) and access that collection within the for loop.

DBManagerDBManager

Thanks again, GreatG, this has been really helpful. What I'm struggling with is finding the right records and assigning them accordingly.

 

The Item record is cloned 'n' times, and for each clone, a different Event/Issue record is assigned. The Event/Issue records must have consecutive months in the 'Date' field.

 

For example:

  • Item A is a subscription to a service that runs from June to August. It is assigned to Event/Issue X.
  • Event/Issue X is dated June, Y is dated July, and Z is dated August.
  • On insert, Item A should be cloned twice, creating Items B and C.
  • The Event Issue records should then be assigned Y -> B and Z -> C.

 

Here is what I have tried, but the Event/Issue record is not updated:

 

trigger SeriesItems on Item__c (after insert) {

Issue_Year__c[] eiList = [SELECT Id, Product__c, Date__c FROM Issue_Year__c];
List<Item__c> itemList = new List<Item__c>();

for (integer i = 0; i < Trigger.new.size(); i++){
    if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE){
        for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++){
            Item__c copy = Trigger.new[i].clone(false);
            copy.Cloned__c = TRUE;
            for (Issue_Year__c ei :eiList){
                Integer a = ei.Date__c.month() + 1 + n;
                if (ei.Id != copy.Event_Issue__c 
                && ei.Product__c == copy.Event_Issue__r.Product__c
                && ei.Date__c.month() == a){
                    copy.Event_Issue__c = ei.Id;
                }
            }
            itemList.add(copy);
        }
    }
}

insert itemList;

}

}

 

Let me know if the above is not clear. Hope you can help.

 

Thanks again.

MiddhaMiddha

Made some changes, please try this:

 

trigger SeriesItems on Item__c (after insert) {

//create a Map of records so that you can access records easily based on the Key i.e. Id
Map<Id,Issue_Year__c> eiList = new Map<Id,Issue_Year__c>([SELECT Id, Product__c, Date__c FROM Issue_Year__c ORDER BY Product__c, Date__c]);

List<Item__c> itemList = new List<Item__c>();

for (integer i = 0; i < Trigger.new.size(); i++)
	{
		if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE)
			{
				for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++)
					{
						Item__c copy = Trigger.new[i].clone(false);
						copy.Cloned__c = TRUE;
						
						//Match dates and not just months, they may be of same months but different years
						//Fetch the original Event Master record and start with the month of that record, this was missing
						Date d = eiList.get(Trigger.new[i].Event_Issue__c).Date__c.addMonths(1 + n);
						for (Issue_Year__c ei :eiList.values())
							{
								if (ei.Id != copy.Event_Issue__c && ei.Product__c == copy.Event_Issue__r.Product__c && ei.monthsBetween(d)==0)								
										copy.Event_Issue__c = ei.Id;								
							}
					itemList.add(copy);
				     }
	        }
	   }

	insert itemList;
}

}

 

DBManagerDBManager

Fantastic - after a very small correction (ei.Date__c.monthsBetween(d)==0), your date calculation seems to be right. I checked this by printing the date, d, in a field in the clone, and it is indeed one month on.

 

However, the trigger as a whole did not seem to work. By eliminating one line at a time, I have found that this line was not working:

&& ei.Product__c == copy.Event_Issue__r.Product__c

Presumably this was to do with the fact that the copy record is virtual at this stage.

 

Therefore, I setup a string variable to get the product from the triggering record and used it thus:

trigger SeriesItems on Item__c (after insert) {

//create a Map of records so that you can access records easily based on the Key i.e. Id
Map<Id,Issue_Year__c> eiList = new Map<Id,Issue_Year__c>([SELECT Id, Product__c, Date__c FROM Issue_Year__c ORDER BY Product__c, Date__c]);

List<Item__c> itemList = new List<Item__c>();

for (integer i = 0; i < Trigger.new.size(); i++)
{
    if (Trigger.new[i].Item_Sold__c == 'CIPI Membership' && Trigger.new[i].Cloned__c == FALSE)
    {
        for (integer n = 0; n < Trigger.new[i].Subscription_Term_Months__c; n++)
        {
            Item__c copy = Trigger.new[i].clone(false);
            copy.Cloned__c = TRUE;
            //Match dates and not just months, they may be of same months but different years
            //Fetch the original Event Master record and start with the month of that record, this was missing
            Date d = eiList.get(Trigger.new[i].Event_Issue__c).Date__c.addMonths(1 + n);
            String p = eiList.get(Trigger.new[i].Event_Issue__c).Product__c;
            for (Issue_Year__c ei :eiList.values())
            {
                if (ei.Id != copy.Event_Issue__c 
                && ei.Product__c == p 
                && ei.Date__c.monthsBetween(d)==0)
                {
                    copy.Event_Issue__c = ei.Id;                                
                }
            }
            itemList.add(copy);
       }
   }
}

if (itemList.size() > 0){
    insert itemList;
    for (Item__c item :itemList){
        item.Line_Number__c = item.Line_Number__c + 1;
    }
    update itemList;
}

}

 

This works, but I will test further and set this as the solution if appropriate.

 

Thanks again, GreatG

This was selected as the best answer