As indicated in the title here is my scenario:

**I am still new to SF/Apex, and I think this is simple, but I'm struggling.

I have an Opportunity Object and that has a related list of Support Contracts. I have a trigger that will create a new Project from the Opportunity when a user checks a box. But what I also need it to do is update the related list of Support Contracts with the newly created Project name.

Here is what I have so far-- I have played around with Map and List, but I'm not getting anywhere.

Thank you!

trigger project_from_opportunity on Opportunity (before update)  { 
List<pse__Proj__c> projectList = new List<pse__Proj__c>();
List<pse__Budget__c> budgetList = new List<pse__Budget__c>();

List<Support_Contract__c> oppsWithServiceContract = new List<Support_Contract__c>([
   SELECT Id, Opportunity__c, Opportunity__r.Name, Product__c, Product_ID__r.pse__IsServicesProduct__c FROM Support_Contract__c
   WHERE (Opportunity__c !=null) AND (Opportunity__c IN :trigger.newMap.keySet()) ]);

FOR(Opportunity opp : Trigger.New){

    Opportunity oldopp = Trigger.oldMap.get(opp.id);
        IF(opp.Create_Project__c == true && oldopp.Create_Project__c == false && oppsWithServiceContract.size()>0){
            projectList.add(new  pse__Proj__c(
            Name = opp.ShortOppName__c + ' ' + opp.Account_Number__c + 'QQQ21'
            ,pse__Account__c = opp.AccountId
            ,pse__Is_Active__c = true
            ,pse__Is_Billable__c = true
            ,CurrencyIsoCode = opp.CurrencyIsoCode
            ,pse__End_Date__c = opp.PSA_Project_End_Date__c
            ,Internal_Notes__c = opp.Comments__c
            ,pse__Opportunity__c = opp.Id
            ,pse__Opportunity_Owner__c = opp.OwnerId
            ,pse__Practice__c = 'a20360000016A7d'
            ,Primary_Sales_Engineer__c = opp.Primary_Sales_Engineer__c
            ,Solutioning_DM__c = opp.Solutioning_DM__r.Name
            ,pse__Project_Status__c = 'Green'
            ,pse__Project_Type__c = 'RMA'
            ,Sales_Engineers__c = opp.Sales_Engineers__c
            ,SRC_Solutioning_Notes__c = opp.SRC_Solutioning_Notes__c
            ,pse__Stage__c = 'Provisional'
            ,pse__Start_Date__c = opp.CloseDate
            //,Order_Type__c = opp.SBQQ__PrimaryQuote__r.
            //,Payment_Terms__c = oppsWithPrimaryQuote.get(opp.Id).Quote__r[0].Payment_Terms__c
            ,Quote__c = opp.SBQQ__PrimaryQuote__r.Name
            ,Opportunity_Record_Type__c = opp.RecordTypeId
            Insert projectList;

        //Because all dependencies are on the Project List the Budget is created without any verification
            budgetList.add(new pse__Budget__c(
            Name = opp.Name
            ,pse__Amount__c = 0.00
            ,pse__Project__c = projectList[0].id
            ,pse__Opportunity__c = projectList[0].pse__Opportunity__c
            ,pse__Account__c = projectList[0].pse__Account__c
            ,pse__Type__c = 'Customer Purchase Order'
            ,pse__Effective_Date__c = projectList[0].pse__Start_Date__c
            ,pse__Status__c = 'Approved'
            ,pse__Approved__c = true
            ,pse__Include_In_Financials__c = true));

            Insert budgetList;

//I am not sure what to do here....

            FOR( Support_Contract__c opps : oppsWithServiceContract ){
                //Delivery Milestones
                supportContractList.add(new Support_Contract__c(
                id = opps.id


                Upsert supportContractList;

2 Answers 2


Normally, if you want to do operations on other objects, you should use after trigger. See this post.

Also best practice is to have only one trigger per object, and put your logic into Apex class(es). For example:

trigger OpportunityTrigger on Opportunity (
    before insert, before update, after insert, after update, before delete, after undelete) {

    if (trigger.isBefore) {
         if (trigger.isInsert) {
         else if (trigger.isUpdate) {
         if (trigger.isDelete) {
    else if (trigger.isAfter) {
         if (trigger.isInsert) {
         else if (trigger.isUpdate) {
            OpportunityClass.onUpdate(trigger.oldMap, trigger.newMap);
         if (trigger.isUndelete) {

Then your OpportunityClass would be something like this:

public class OpportunityClass {
    public static void onUpdate(Map<Id, Opportunity> oldMap, Map<Id, Opportunity> newMap) {
        // codes...


Now, to your question:

I have a trigger that will create a new Project from the Opportunity when a user checks a box. But what I also need it to do is update the related list of Support Contracts with the newly created Project name.

You might want to consider breaking your logic into two parts:

  1. Opportunity Trigger, on after update, insert the Project record
  2. Project Trigger, on after insert, upsert the Support Contracts

Then Project trigger would be something like this:

trigger ProjectTrigger on pse__Proj__c (after insert) {

    if (trigger.isAfter && trigger.isInsert) {
        // Note: this whole thing below should be in an Apex class instead

        // collect the list of Opportunity IDs
        Set<Id> oppIds = new Set<Id>();
        for (pse__Proj__c p : trigger.new) {

        // only perform if OppIds is not empty
        if (!oppIds.isEmpty()) {

            // query the opportunities with their Support Contracts
            // use child relationship query to get the Support Contracts
            // I assume the child relationship name is Support_Contracts
            Map<Id, Opportunity> oppWithSupportContracts = new Map<Id, Opportunity>([
                SELECT Id, (SELECT Id FROM Support_Contracts__r) 
                FROM Opportunity WHERE Id = :oppIds]);

            List<Support_Contract__c> supportContractsToUpdate = new List<Support_Contract__c>();
            for (pse__Proj__c p : trigger.new) {
                if (oppWithSupportContracts.containsKey(p.pse__Opportunity__c)) {
                    List<Support_Contract__c> supportContracts = 
                    if (supportContracts.isEmpty()) {
                        // create a new Support Contract if there isn't one
                            new Support_Contract__c(
                                // assign the fields name value here
                                // sc.Opportunity__c = p.pse__Opportunity__c;
                                // sc.field1 = p.field1;
                                // sc.field2 = p.field2;
                                // ...
                    else {
                        // update existing Support Contracts
                        for (Support_Contract__c sc : supportContracts) {
                            // assign the fields name value here
                            // sc.field1 = p.field1;
                            // sc.field2 = p.field2;


Please note that there are some violations in your code against Apex coding best practice, eg: SOQL and DML inside a loop. See Apex Coding Best Practise.


I would say try something like this in your trigger code:

    trigger project_from_opportunity  on Opportunity (before update, before insert) {

    if(Trigger.isAfter || Trigger.isUpdate){
        for(Support_Contract__c sp : [Select Id,Name,(Select Id,Name From pse__Proj__r) From Support_Contract__c Where Opportunity__c in :Trigger.newMap.keySet()]){

            //Logic on the Support_Contract__c 'sp'  goes here...

            for(pse__Proj__c  psecontract : sp.pse__Proj__r){
                //Logic on the current child pse__Proj__c  psecontract  goes here...

something similar to this: https://developer.salesforce.com/forums/?id=906F00000005H2SIAU

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .