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
azar khasimazar khasim 

Need help in writing trigger between Opportunity and Project(Custom Object) to create Project with specified Template(Record).

Hello Everyone,

I need help with my requirements.

Need help in writing a trigger between Opportunity and Project(Custom Object) to create Project wit specified Template(Record).

I have three objects.
Opportunity (Three Checkboxes)(Temp 60, Temp 90, Temp 120)
      ||
Project (Record Type -- ST Templates) Records(Temp 60, Temp 90, Temp 120)
      ||
MIlestones


criterias..,

1. When the Opportunity Stage is ClosedWon, create a Project.
2. The opportunity has three checkboxes names
    (Three Checkboxes -- Temp 60, Temp 90, Temp 120)
3. The project has Record Type as (Record Type -- DC Project Templates)
   These are the ST templates (Means records in Project Object with Record Type -- DC Project Templates) -- Temp 60, Temp 90, Temp 120.

Now, Whenever Opportunity is ClosedWon and Checkbox Temp 60 is true.
Create a New Project with Selecting Temp60 as the default template for Project because Temp 60 has Child Records(28 Milestones in Milestones Object.)



Please Help me in providing a trigger for two objects.

Trigger :
Opportunity Stage:  ClosedWon && Temp 60 Checkbox is true 
Create a Project with  Dc Project Template (Temp 60) as default.

If I select Temp 60 checkbox in Opportunity Temp 60 Record from Project is to be the default. Because of Every Template(Record) in the DC Project Template as a certain number of unique Child Records.

If I select Temp 90 checkbox in Opportunity Temp 90 Record from Project is to be the default. Because of Every Template(Record) in the DC Project Template as a certain number of unique Child Records.

If I select Temp 120 checkbox in Opportunity Temp 120 Record from Project is to be the default. Because of Every Template(Record) in the DC Project Template as a certain number of unique Child Records.


Please Provide me the trigger for this scenario.

Thanks and Regards,
Azar Khasim.



 
Best Answer chosen by azar khasim
Christan G 4Christan G 4
Hi Azar, I hope you are well. Sorry for the late reply. I fell asleep. I am unsure as to what you meant when you say there are different types of days and how that corresponded to the Relative Start Day and Relative End Day.

Is it like if Insert 60 Days is chosen, will the Relative Start Day be todays date and the Relative End Day would be 60 days after that?

With that being said, I provided the general outline of the code for this requirement below. All I need for you to do is input the API fields for the fields you mentioned and assign them their values. If you need help, feel free to contact me.

Please note that I literally wrote this code just now without testing so I apologize if you experience any issues. If you do, please feel free to reach out to me so I can resolve them.

Please paste the code within Apex Class to a new Apex Class within your sandbox and make the necessary changes. Afterwards, please copy and paste the code within Apex Trigger to a new Apex Trigger in your sandbox. 

Apex Class:
public class ProjectTriggerHelper {

public static String genRanID(Integer num) { 

final String ranChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; 
String genRand = ''; 

while (genRand.length() < num) { 
Integer index = Math.mod(Math.abs(Crypto.getRandomInteger()), ranChars.length()); 
genRand += ranChars.substring(index, index+1); } 
return genRand; 

}

public static void autoCreateMilestone (List <Milestone1_Project__c> proj) {

//List to store new Milestones
List <Milestone1_Milestone__c> newMiles = new List <Milestone1_Milestone__c>();

//Map to store Record Types for Project Object
Map <ID, Schema.RecordTypeInfo> projSchemaMap = Milestone1_Project__c.sObjectType.getDescribe().getRecordTypeInfosByID();

//Record Type ID for DC Milestone Template
ID dcMileTemp = Milestone1_Milestone__c.sObjectType.getDescribe().getRecordTypeInfosByName().get('DC Project Milestone Template').getRecordTypeID();

for (Milestone1_Project__c oneProj : proj) {

Boolean projRecCheck = projSchemaMap.get(oneProj.RecordTypeID).getName().equals('DC Project Template');

//Note: Not sure if correct API name for checkbox was used. Please correct if needed
if (oneProj.Insert_60_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {
            
            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}

if (oneProj.Insert_90_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

           //Generates unique ID for each milestone
           String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}

if (oneProj.Insert_120_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}
}

if (newMiles.size() > 0) {

insert newMiles;

}
}
}

Apex Trigger:
trigger ProjectTrigger on Milestone1_Project__c (after update) {

ProjectTriggerHelper.autoCreateMilestone(Trigger.new);

}
Feel free to inform me if have any questions.

 

All Answers

Christan G 4Christan G 4
Hi Azar, I am little confused regarding how the Project and Milestone object are set up. If one was to manually create a new Project, would they have to choose between these 3 record types: Temp 60, Temp 90, or Temp 120? If yes, you  basically want to automate this process based on which of the 3 checkboxes are checked when an opportunity is marked as Closed Won?
azar khasimazar khasim
Hello Christan,

Sorry, for making you confuse about my criteria.
I have found 30% yesterday.
Now I need some help for the remaining 70% to get complete.

I am taking three objects
Opportunity 
Project (API Name: Milestone1_Project__c)
Milestones  (API Name : Milestone1_Milestone__c)

Now Criteria:

Opportunity is ClosedWon. I am creating a project manually.
Project object has Three record types 
1. Internal Work 
2. Project Template
3. DC Project Template.

Now I am using Third record type (DC Project Template):

Till here everything Clear,.....
*********************************************************************************************************************************************
Now I have three checkboxes in Project Object
1. Insert 60 days Milestones
2. Insert 90 days Milestones
3. Insert 120 days Milestones

For example when I select Insert 60days Milestones = True.

In Milestone Object, I need to create 28 Records with unique Milestone names. With Record Type as (DC Project Milestone Template)

Like that, if I select 90 days, a different set of 28 Records needs to create.

Because I have some fields(Relative Start day and Relative End Day) with different numbers for these different days like 60 days, 90 days and 120 days.

In Milestones Object I have fields like

Milestone Name
Project (Master-Detail Relation)
Predecessor Milestone (Milestone Lookup)
Successor Milestone (Milestone Lookup)
Relative Start day
Relative End day
Order 
Type 
Assign to Role
Assigned to (User Lookup)


So please help me to write a trigger for creating Milestones for a Project which has a record type as DC Project Template.

Trigger on Project Object which creates Milestones(Child Object for Project) as records in Milestone Object.

Please give me a solution for this criteria.

Thanks and Regards,
Azar Khasim. 
Christan G 4Christan G 4
Hi Anzar, thank you so much for clarifying. Just to ensure I understood, I have provided an example scenario below:

Scenario:

1 - There is an opportunity with Temp 60 checked and is still active. One day, user logs in and changes its stage to Closed Won.

2 - As soon as it is change, you would like for the a Project record to be automatically created with the DC Project Template record type. You would also like for Insert 60 days Milestones checkbox on the new project record type to also be checked. (Are there checkboxes on both opportunity and project?)

3 - Finally create 28 Milestone records with unqiue names and with DC Project Milestone Template as their record type.

Please confirm my understanding is correct when possible. Thanks in advance!
azar khasimazar khasim
Yes Christan,

You are right in my flow.
When opportunity is closedwon 
As a user I am  create a project with record type as DC Project Template  manually no need to automate now.
Till Project creation no automation required.

Then user will open the project then he/she will manually click on checkbox
Insert 60 days Milestone
Or
Insert 90 days Milestone
Or
Insert 120 days Milestone
then 
Trigger have to fire and create 28 milesones as records in Milesone Object. With record type as (DC Project Milestone Template)
Remember as I have mentioned in my above message about the fields in Milesone Object as

Milestone Name
Project (Master-Detail Relationship)
Assign to Role
Assigned to (User Loopup)
Predecessor Milestone (Milestone Lookup)
Successor Milestone (Milestone Lookup)
Relative Start day
Relative End day
Planned Start date
Planned End date
Type
Order ( 0-27) total 28 records

Relative Start day and Relative End day is different for
Three types of days(60 days Milestone , 90 days Milestone, 120 days Milestone)

Hence I need to create based on these changes.
When user clicked Insert 60 days Milestone in Project Object 
Create 28 milestones of 60 days format.

So please provide a. Solution for this criteria Christan

Thanks and Regards,
Azar Khasim.
Christan G 4Christan G 4
Hi Azar, I hope you are well. Sorry for the late reply. I fell asleep. I am unsure as to what you meant when you say there are different types of days and how that corresponded to the Relative Start Day and Relative End Day.

Is it like if Insert 60 Days is chosen, will the Relative Start Day be todays date and the Relative End Day would be 60 days after that?

With that being said, I provided the general outline of the code for this requirement below. All I need for you to do is input the API fields for the fields you mentioned and assign them their values. If you need help, feel free to contact me.

Please note that I literally wrote this code just now without testing so I apologize if you experience any issues. If you do, please feel free to reach out to me so I can resolve them.

Please paste the code within Apex Class to a new Apex Class within your sandbox and make the necessary changes. Afterwards, please copy and paste the code within Apex Trigger to a new Apex Trigger in your sandbox. 

Apex Class:
public class ProjectTriggerHelper {

public static String genRanID(Integer num) { 

final String ranChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; 
String genRand = ''; 

while (genRand.length() < num) { 
Integer index = Math.mod(Math.abs(Crypto.getRandomInteger()), ranChars.length()); 
genRand += ranChars.substring(index, index+1); } 
return genRand; 

}

public static void autoCreateMilestone (List <Milestone1_Project__c> proj) {

//List to store new Milestones
List <Milestone1_Milestone__c> newMiles = new List <Milestone1_Milestone__c>();

//Map to store Record Types for Project Object
Map <ID, Schema.RecordTypeInfo> projSchemaMap = Milestone1_Project__c.sObjectType.getDescribe().getRecordTypeInfosByID();

//Record Type ID for DC Milestone Template
ID dcMileTemp = Milestone1_Milestone__c.sObjectType.getDescribe().getRecordTypeInfosByName().get('DC Project Milestone Template').getRecordTypeID();

for (Milestone1_Project__c oneProj : proj) {

Boolean projRecCheck = projSchemaMap.get(oneProj.RecordTypeID).getName().equals('DC Project Template');

//Note: Not sure if correct API name for checkbox was used. Please correct if needed
if (oneProj.Insert_60_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {
            
            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}

if (oneProj.Insert_90_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

           //Generates unique ID for each milestone
           String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}

if (oneProj.Insert_120_days_Milestones == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'Milestone #' +uniqueId,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id));
}
}
}

if (newMiles.size() > 0) {

insert newMiles;

}
}
}

Apex Trigger:
trigger ProjectTrigger on Milestone1_Project__c (after update) {

ProjectTriggerHelper.autoCreateMilestone(Trigger.new);

}
Feel free to inform me if have any questions.

 
This was selected as the best answer
azar khasimazar khasim
Hello Christan,

No need of sorry, Actually happy about you for helping me with this complex trigger.

Actually, I have done some updates on it like adding correct API like that.

But Line 24 column 1 is not allowing me to create a project manually.
Below is the error message I received

Error Message:
ProjectTrigger: execution of AfterUpdate caused by: System.NullPointerException: Attempt to de-reference a null object Class.ProjectTriggerHelper.autoCreateMilestone: line 24, column 1 Trigger.ProjectTrigger: line 3, column 1


Please have a look of it Christan,

Thanks and Regards,
Azar Khasim. 
Christan G 4Christan G 4
Thank You Anzar for your feedback. Can you copy and paste the code at line 24 so I know which line in particular is giving an error?
azar khasimazar khasim
Sure Christan,

 //Record Type ID for DC Milestone Template
        ID dcMileTemp = Milestone1_Milestone__c.sObjectType.getDescribe().getRecordTypeInfosByName().get('DC Project Milestone Template').getRecordTypeID();
Christan G 4Christan G 4
Thank You! Are you sure that the name of the record type is exactly named “DC Project Milestone Template”? If it isn’t, then it’ll return a null value.
azar khasimazar khasim
Ya Christan,

You are right, It's my mistake.
Thankyou.
Christan G 4Christan G 4
No worries! If you experience any other issue, feel free to reach out to me.
azar khasimazar khasim
Hello Christan,

I got some other error while creating Milestones as

Error Message:

ProjectTrigger: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, Milestone1_Milestone_Trigger: maximum trigger depth exceeded Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert Milestone1_Project trigger event AfterUpdate Milestone1_Milestone trigger event AfterInsert: [] Class.ProjectTriggerHelper.autoCreateMilestone: line 91, column 1 Trigger.ProjectTrigger: line 3, column 1

Line 91 Column 1:
 
91 line code is :

     insert newMiles;


Can you please have a look. I think some other trigger is stopping this Trigger. 

 
Christan G 4Christan G 4
Hi Azar, I think you are correct that another trigger is conflicting with this one. Are there any active triggers on the Milestone object? If yes, you can temporarily disable them via the Setup menu and just ensure that the trigger I wrote is working as expected. Afterwards, we can reactive the trigger and troubleshoot the issue.
azar khasimazar khasim
Yes Christan,

You are right. There are two triggers which are stopping to create record.
One trigger was written on Project Object only for creation of Milestones but for different RecordType(I dont have permisssion to change the code.)

Now i have disabled the both triggers but still facing the same issue.

Error Message:

ProjectTrigger: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, ProjectTrigger: maximum trigger depth exceeded Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate Milestone1_Project trigger event AfterUpdate: [] Class.ProjectTriggerHelper.autoCreateMilestone: line 91, column 1 Trigger.ProjectTrigger: line 3, column 1

I think i need to use New Custom Object for solving this issue, because One trigger for one object is a good practice.

 
azar khasimazar khasim
Yes Christan,

It's Working. Records Created.
But one thing while using in New Custom Object 28 records created with the same name but I want to create with different names for 28 Records.

So can you tell me In which IF Condition I need to change or add any criteria for different names and different values in different fields!

Please help me with this now. 
Christan G 4Christan G 4
Hi Anzar, make sure that the name field equals “Milestone #”+ uniqueID. It should create a unique name for each record that it creates.
azar khasimazar khasim
Yes Christan,

I will provide you an example for 60 days Milestones.

Milestone 1                              Milestone 2                     Milestone 3                   
Name: BBC                              Name: KJU                     Name: IKOP                      
Type:  DC                                 Type: NM                        Type: IKOP
Related Start day: 0                Related Start day: 3        Related Start day: 7
Related End day: 2                Related End day: 6         Related End day: 13
Order : 0                                 Order: 1                          Order: 2

Like in above it should be.
Please make the required change for me to Unique Name for the records.
I will Enter the names in the code but provide me option that with one name one record.

Please change the code in FOR Loop like create First record then the second record then the third record then the fourth record like that.
Please provide me that change Christan.
Christan G 4Christan G 4
Hi Azar, in regards to your request, I have a few questions.

1 - What formula is used to calculate the start date and end day for 60, 90, 120 days? I wasn't able see a pattern for 60 days.
2 - Why not convert the required name field to an autonumber field? It would automatically attach a unique number to each milestone.
3 - What determines the name of a milestone? Do you always want it to be random?
4 - What determines the type of a milestone?

Regarding making the required change to make the name unique, I did already in the code I provided.
azar khasimazar khasim
Yes Christan,
Relative Start day and End day I will give some values in the code based on which milestone I am using and number of milestones I am using.

I have 28 different names for 28 records that I need to create.
​​​​​​
Type field has nearly 20 different values in picklist where I need to use for the milestones.

I can't use Autonumber here.

So that is the reason why I am asking how to change the code that I will provide names for all milestones and type value like every field I need to give different values based on different days of using it.

Please provide me the change that we need to do for that
FOR Loop where you have given as 
for( I=0, I<28, I++)
What need to change for that and changes if any..

Please provide me a solution Christan

Thanks and Regards,
Azar Khasim
 
Christan G 4Christan G 4
Hi Azar, I modified the code within the if statements and for loops with the additional fields you wanted. Please put the values you want within each if block depending on the days checked.
 
if (oneProj.Insert_60_days_Milestones__c == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {
            
            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'PUT NAME YOU CHOOSE HERE' +uniqueId,
                                                     Type__c = 'PUT PICKLIST YOU CHOOSE HERE',
                                                     Related_Start_day = PUT VALUE INTEGER VALUE YOU CHOOSE HERE,
                                                     Related_End_Day = PUT VALUE INTEGER VALUE YOU CHOOSE HERE, 
                                                     RecordTypeID = dcMileTemp, 
                                                     Project = oneProj.id, 
                                                     Order__c = i));
}
}

if (oneProj.Insert_90_days_Milestones__c == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

           //Generates unique ID for each milestone
           String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them
            newMiles.add(new Milestone1_Milestone__c(Name = 'PUT NAME YOU CHOOSE HERE' +uniqueId,
                                                     Type__c = 'PUT PICKLIST YOU CHOOSE HERE',
                                                     Related_Start_day = PUT VALUE INTEGER VALUE YOU CHOOSE HERE,
                                                     Related_End_Day = PUT VALUE INTEGER VALUE YOU CHOOSE HERE,
                                                     RecordTypeID =  dcMileTemp,
                                                     Project = oneProj.id,
                                                     Order__c = i));
}
}

if (oneProj.Insert_120_days_Milestones__c == true && projRecCheck == true) {

           for (Integer i = 0; i < 28; i++) {

            //Generates unique ID for each milestone
            String uniqueId = ProjectTriggerHelper.genRanID(9);

//Please put the API names of the other fields you mentioned and if possible, assign them 

              newMiles.add(new Milestone1_Milestone__c(Name = 'PUT NAME YOU CHOOSE HERE' +uniqueId,
                                                       Type__c = 'PUT PICKLIST YOU CHOOSE HERE',
                                                       Related_Start_day__c = PUT VALUE INTEGER VALUE YOU CHOOSE HERE,
                                                       Related_End_Day__c = PUT VALUE INTEGER VALUE YOU CHOOSE HERE, 
                                                       RecordTypeID = dcMileTemp, 
                                                       Project = oneProj.id, Order__c = i));
}
}
}

if (newMiles.size() > 0) {

insert newMiles;

}
}
}
azar khasimazar khasim
Hello Christan,

Sorry I fell asleep because in India it's 1:30 in the morning at that time.

Ooo Nice thank you Christan. I have seen the code. It's working.
Thank you for ur help Christan.

Thanks and Regards,
Azar Khasim
Christan G 4Christan G 4
Anytime! And no worries. I am glad that I was able to help. Please mark my answer as the best answer to mark this issue as solved.