1

I have a requirement to Create contracts and assets from Opportunites and lineitems when the opp is closed

So far the logic works well, i am having issue linking the newly created assets with the contract

The contract and assets are created in separate for loops and i cannot get the id for the contract into the loop that is creating the assets

ass.Contract__c = newcontracts[0].id;

Trying to get the id from the list is the closet i have come as debug on the list shows the id, however this doesnt work

trigger

trigger CreateContracts on Opportunity (After Update) {



set<Id> oppIds = new set<id>();
map<id,opportunity> Accountid = New map<id,opportunity>();

for(Opportunity o : trigger.new){
    if(o.isWon && !trigger.oldMap.get(o.Id).isWon){
        oppIds.add(o.Id);
        accountid.put(o.Id, o);
    }
}
list<Opportunity> myOpps = [Select Id, Name, StageName, OwnerId, AccountId, CloseDate, Amount
                                From Opportunity 
                                Where Id In : oppIds];

system.debug(myOpps);

list<Opportunitylineitem> myOppLines = [Select Id, opportunityid, product2id, unitprice,name
                                From Opportunitylineitem 
                                Where opportunityId In : oppIds];

//if(oppIds.size()>0){Id Account = [SELECT id,accountid From Opportunity where id IN : oppids].accountid;}

//System.debug(account);


list<Contract> newContracts = new list<Contract>();  
list<Asset> newAsset= new list<Asset>();  

for(Opportunity o : myOpps){
    Contract Con = New Contract();
    con.AccountId = o.AccountId;
    con.Status = 'Draft';




    newContracts.add(Con);
}

insert newContracts;

system.debug(newContracts);

//if contractid on opp isblank integer i = 0 while less that opp line size create asset for each line

For(Integer i = 0; i < myOppLines.size() ; i++){


  Asset Ass = New Asset();

  Ass.Product2Id = myOppLines[i].product2id;
  ass.Name = myOppLines[i].name;
  ass.AccountId = Accountid.get(myOppLines[i].opportunityid).accountid;
  ass.Contract__c = newcontracts[0].id;

  NewAsset.add(Ass);  

} 





    Insert NewAsset;

}
  • Is there a relationship between Contracts and Opptys? But id separate these triggers out.. developer.salesforce.com/page/… – EricSSH Aug 22 '17 at 17:46
  • though you have another good answer, check my approach just in case if you have other thoughts – Santanu Boral Aug 22 '17 at 18:14
1

What you need to do is tie the new Contracts being created to an Id that you can access from each OpportunityLineItem. A Map can help you accomplish this.

// Instead of creating a list to hold new Contracts, make it a map
// The key of the map can be something other than the Id of the Contract (which doesn't
//   exist at this point in time)
Map<Id, Contract> oppIdToContract = new Map<Id, Contract>();
for(Opportunity o : myOpps){
    Contract con = New Contract();
    con.AccountId = o.AccountId;
    con.Status = 'Draft';

    oppIdToContract.add(o.Id, con);
}

// We can't DML on a Map, but we can DML on the list returned by map.values()
// DML insert will cause the record instances passed to it to be updated with the 
//   Id after it finishes.
// Because .values() returns a list that contain references to the same in-memory
//   object instances that are stored in the map, this means that the Contract
//   records stored in the map will have their Id populated after the DML finishes.
insert oppIdToContract.values();

for(OpportunityLineItem oli :myOppLines){
    Asset ass = New Asset();

    // You queried OpportunityId for OpportunityLineItem, so we can use that to
    //   pull the corresponding Contract from our map.
    ass.ContractId = oppIdToContract.get(oli.OpportunityId).Id;
}
1

Best way to handle this use case is to insert records based on ExternalId.

With a single insert statement you could insert both Parent and Child records.

Create an external Id field on Contract Object and insert Contract and Assets record in a single insert.

Here is the approach.

trigger CreateContracts on Opportunity (After Update) {

    //keep your existing code as it was upto this line

    Map<String,String> contractOpptyMap = new Map<String,String>(); //it will hold OpportunityId and ExternalId                         

    list<Contract> newContracts = new list<Contract>();  
    list<Asset> lstNewAsset= new list<Asset>();  

    //build records from Opportunities.
    for(Opportunity o : myOpps)
    {
        Contract con = New Contract();
        con.AccountId = o.AccountId;
        con.Status = 'Draft';
        con.External_Id__c = String.ValueOf(Integer.valueOf(math.rint(math.random()*1000000)));
        contractOpptyMap.put(o.Id,con.External_Id__c);
        newContracts.add(Con);
    }

    //build records from Opportunity line items
    for(Integer i = 0; i < myOppLines.size() ; i++)
    {
        Asset AssetObj = New Asset();

        AssetObj.Product2Id = myOppLines[i].product2id;
        AssetObj.Name = myOppLines[i].name;
        AssetObj.AccountId = Accountid.get(myOppLines[i].opportunityid).accountid;

        //map the reference
        Contract conReference = new Contract(
        External_Id__c =contractOpptyMap.get(myOppLines[i].OpportunityId)); 
        AssetObj.Contract = conReference;

        lstNewAsset.add(AssetObj);  

    }

    SObject[] sobjList = new List<SObject>(); 
    sobjList.addAll(newContracts);
    sobjList.addAll(lstNewAsset);   
    Database.SaveResult[] results = Database.insert(sobjList);
}

For more information, refer my blog post: Insert Multiple Parent and Child Records with External Id

And Creating Parent and Child Records in a Single Statement Using Foreign Keys documentation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.