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
Adriana Smith 9Adriana Smith 9 

Need Help with Trigger Error

Hello, I'm building a trigger that I had at a previous company where an asset is populated on the account, from the listed products on an Opportunity, anytime it is Closed Won.
the trigger is:

trigger CreateAssetonClosedWon on Opportunity (after insert, after update) {
     for(Opportunity o: trigger.new){ 
      if(o.isWon == true && o.HasOpportunityLineItem == true){
         String opptyId = o.Id;
         OpportunityLineItem[] OLI = [Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Project_Association__c, Product_Family__c, Product_Category__c, Converted_to_Asset__c  
                                      From OpportunityLineItem 
                                      where OpportunityId = :opptyId  and Converted_to_Asset__c = false];
         Asset[] ast = new Asset[]{};
         Asset a = new Asset();
         for(OpportunityLineItem ol: OLI){
            a = new Asset();
            a.AccountId = o.AccountId;
            a.Product2Id = ol.PricebookEntry.Product2Id;
            a.Quantity = ol.Quantity;
            a.Price =  ol.UnitPrice;
            a.PurchaseDate = o.CloseDate;

The error I'm getting is: Error: Compile Error: expecting right curly bracket, found '<EOF>' at line 17 column 0

I added a curly bracket on line 17, so it pushed the error to line 18.  What am I missing?
Best Answer chosen by Adriana Smith 9
Anurag SaxenaAnurag Saxena
Trigger CreateAssetonClosedWon on Opportunity (after insert, after update){
Set<string> OppsIds = new set<string>();
Set<string> accIds = new set<string>(); 
List<Asset> assets = new List<Asset>();
    For(opportunity o : trigger.new)
    {
        OppsIds.add(o.id);
        accIds.add(o.accountid);
    }

Map<id, opportunityLineItem> mapOpp = new Map<id, opportunityLineItem>([select Id, unitprice, quantity,pricebookentry.product2id from opportunityLineItem where opportunityId IN : OppsIds and converted_to_Asset__c = false]);
Contact con = [Select id from Contact where accountid =: accIds order by CreatedDate limit 1 ];

    For(opportunity opp : trigger.new)
    {
        For (opportunityLineItem Oli : mapOpp.values())
        {
            If(opp.iswon == true && opp.hasopportunitylineitem ==true)
               {
                Asset a = new asset();
                a.name = a.Opportunity_Name__c;
                a.accountid = opp.Accountid;
                a.contactid = con.id;
                a.product2id = Oli.pricebookentry.product2id;
                a.quantity = Oli.quantity;
                a.price = Oli.unitprice;
                a.purchasedate = opp.closedate;
                
                assets.add(a);
                }
        }
    }

           If (assets.size()>0)
               Insert assets;
}

Please try this I have made the changes:))

let me know what happens?

Thanks

All Answers

Anurag SaxenaAnurag Saxena
Hi Adriana,

I have removed the Compile error.

Try it once 

 
trigger CreateAssetonClosedWon on Opportunity (after insert, after update) {
    for(Opportunity o: trigger.new)
	{ 
		if(o.isWon == true && o.HasOpportunityLineItem == true)
		{
			String opptyId = o.Id;
        OpportunityLineItem[] OLI = [Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Project_Association__c, Product_Family__c, Product_Category__c, Converted_to_Asset__c 
									From OpportunityLineItem 
                                    Where OpportunityId = :opptyId  and Converted_to_Asset__c = false];
        Asset[] ast = new Asset[]{};
        Asset a = new Asset();
    for(OpportunityLineItem ol: OLI)
	{
        a = new Asset();
        a.AccountId = o.AccountId;
        a.Product2Id = ol.PricebookEntry.Product2Id;
        a.Quantity = ol.Quantity;
        a.Price =  ol.UnitPrice;
        a.PurchaseDate = o.CloseDate;
		insert a;
	}
		}
	}
}

let me know what happens

Thanks:)
Shivdeep KumarShivdeep Kumar
Hi Smith,
why are you using soql inside the for loop.
please try the below  code ...
Trigger CreateAssetonClosedWon on Opportunity (after insert, after update){
Set<string> OppsIds = new set<string>();
For(opportunity o : trigger.new){

OppsIds.add(o.id);

}
List<Asset> assets = new List<Asset>();
Map<id, opportunityLineItem> mapOpp = new Map<id, opportunityLineItem>([select Id, unitprice, quantity from opportunityLineItem where opportunityId IN : OppsIds and converted_to_Asset__c = false]);

For(opportunity opp : trigger.new){
For (opportunityLineItem Oli : mapOpp.values()){
If(opp.iswon == true && opp.hasopportunitylineitem ==true){
Asset a = new asset();
a.Accountid = opp.Accountid;
a.product2id = Oli.pricebookentry.product2id;
a.quantity = Oli.quantity;
a.price = Oli.unitprice;
a.purchasedate = opp.closeddate;
assets.add(a);
}
}
}

If (assets.size()>0)
Insert assets;
}
Please let me know, if this help...

thanks
shivdeep

.
Adriana Smith 9Adriana Smith 9
Triggering this message when you try to mark the opportunity as closed won:

Error:Apex trigger CreateAssetonClosedWon caused an unexpected exception, contact your administrator: CreateAssetonClosedWon: execution of AfterUpdate caused by: System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: OpportunityLineItem.PricebookEntry: Trigger.CreateAssetonClosedWon: line 16, column 1
Shivdeep KumarShivdeep Kumar
Hi smith ,
please query all the fields at line no. 9 which is used for updation like pricebookentry, product2, opportunityId and related fields of opportunitylineitem object.

thanks
shivdeep
Shivdeep KumarShivdeep Kumar
Hi Smith,
use this query at line no.9
Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Description, Converted_to_Asset__c  
                                  From OpportunityLineItem

thanks
Shivdeep
Adriana Smith 9Adriana Smith 9
Like this?

Trigger CreateAssetonClosedWon on Opportunity (after insert, after update){
Set<string> OppsIds = new set<string>();
For(opportunity o : trigger.new){

OppsIds.add(o.id);

}
List<Asset> assets = new List<Asset>();
Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Description, Converted_to_Asset__c  
                                  From OpportunityLineItem

For(opportunity opp : trigger.new){
For (opportunityLineItem Oli : mapOpp.values()){
If(opp.iswon == true && opp.hasopportunitylineitem ==true){
Asset a = new asset();
a.Accountid = opp.Accountid;
a.product2id = Oli.pricebookentry.product2id;
a.quantity = Oli.quantity;
a.price = Oli.unitprice;
a.purchasedate = opp.closedate;
assets.add(a);
}
}
}

If (assets.size()>0)
Insert assets;
}
Shivdeep KumarShivdeep Kumar
Hi Smith,
no...
lIke this....
Map<id, opportunityLineItem> mapOpp = new Map<id, opportunityLineItem>([Select UnitPrice, Quantity, PricebookEntry.Product2Id, PricebookEntry.Product2.Name, Description, Converted_to_Asset__c   From OpportunityLineItem]);

thanks
Adriana Smith 9Adriana Smith 9
Error:Apex trigger CreateAssetonClosedWon caused an unexpected exception, contact your administrator: CreateAssetonClosedWon: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Asset Name, Opportunity]: [Asset Name, Opportunity]: Trigger.CreateAssetonClosedWon: line 26, column 1
Shivdeep KumarShivdeep Kumar
Hi Smith,
are you running this trigger without adding any product related to the opportunity?
This trigger is working fine on my org. Please check your opportunity record.
You can do one more thing that between line no.14 and 15 add 
a.name = Oli.pricebookentry.product2.Name;
If it ll not work then you are doing a wrong process.
Thanks
 
Adriana Smith 9Adriana Smith 9
I have an opportunity with 2 products listed.  When I go to mark it as 'closed won', thereby creating the new assets, it's triggering the message:

Error:Apex trigger CreateAssetonClosedWon caused an unexpected exception, contact your administrator: CreateAssetonClosedWon: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Opportunity]: [Opportunity]: Trigger.CreateAssetonClosedWon: line 27, column 1

I added your line above between 14 and 15 and its still not working.
Shivdeep KumarShivdeep Kumar
Can you please share the screenshots?
Adriana Smith 9Adriana Smith 9
User-added image
Anurag SaxenaAnurag Saxena
Hi Adriana,

you are getting this error because you have missed defining one of  the required fields on assets.

Please check that by creating an asset through UI

So you can know what all fields are required to create assets and then check all the fields are present in the trigger asset instance or not

Hope it will help you 

Thanks:)
Adriana Smith 9Adriana Smith 9
Thank you, so the only required fields  opportunity- where/how do I add that?
Anurag SaxenaAnurag Saxena
@adriana you need to add the required field like this 
a.Accountid = opp.Accountid;
a.product2id = Oli.pricebookentry.product2id;
a.quantity = Oli.quantity;
a.price = Oli.unitprice;
a.purchasedate = opp.closedate;
a.required_field = opp.field_of which_you_value_want_in_this_required_field
assets.add(a);

let me know what happens?

Thanks
Adriana Smith 9Adriana Smith 9
I'm sorry, I have tried this every which way.  So I created a custom field on assets: Opportunity_Name__c, that is a rollup field to pull the Opportunity Name that the product is listed on.  This field is what is required, so that on the account page you have a list of the assets, and their corresponding opportunity.  But I can't get the formula to work for me. I'm clearly not entering it correctly.
 
Anurag SaxenaAnurag Saxena
Try this it will surely work now :)
Trigger CreateAssetonClosedWon on Opportunity (after insert, after update){
Set<string> OppsIds = new set<string>();
Set<string> accIds = new set<string>(); 
List<Asset> assets = new List<Asset>();
    For(opportunity o : trigger.new)
    {
        OppsIds.add(o.id);
        accIds.add(o.accountid);
    }

Map<id, opportunityLineItem> mapOpp = new Map<id, opportunityLineItem>([select Id, unitprice, quantity,pricebookentry.product2id from opportunityLineItem where opportunityId IN : OppsIds and converted_to_Asset__c = false]);
Contact con = [Select id from Contact where accountid =: accIds order by CreatedDate limit 1 ];

    For(opportunity opp : trigger.new)
    {
        For (opportunityLineItem Oli : mapOpp.values())
        {
            If(opp.iswon == true && opp.hasopportunitylineitem ==true)
               {
                Asset a = new asset();
                a.name = opp.name;
                a.accountid = opp.Accountid;
                a.contactid = con.id;
                a.product2id = Oli.pricebookentry.product2id;
                a.quantity = Oli.quantity;
                a.price = Oli.unitprice;
                a.purchasedate = opp.closedate;
                
                assets.add(a);
                }
        }
    }

           If (assets.size()>0)
               Insert assets;
}
Please choose as best answer

Thanks:)
Adriana Smith 9Adriana Smith 9
Almost! It's not triggering an error message anymore, and it's populating the assets on the accounts perfectly, it's just putting the opportunity that the products came from (Opportunity_Name__c) under the asset name instead of on the field under asset called Opportunity (Opportunity_Name__c)... same field/api name.  Maybe I'm making this too difficult? I just want it to map.
Anurag SaxenaAnurag Saxena
Trigger CreateAssetonClosedWon on Opportunity (after insert, after update){
Set<string> OppsIds = new set<string>();
Set<string> accIds = new set<string>(); 
List<Asset> assets = new List<Asset>();
    For(opportunity o : trigger.new)
    {
        OppsIds.add(o.id);
        accIds.add(o.accountid);
    }

Map<id, opportunityLineItem> mapOpp = new Map<id, opportunityLineItem>([select Id, unitprice, quantity,pricebookentry.product2id from opportunityLineItem where opportunityId IN : OppsIds and converted_to_Asset__c = false]);
Contact con = [Select id from Contact where accountid =: accIds order by CreatedDate limit 1 ];

    For(opportunity opp : trigger.new)
    {
        For (opportunityLineItem Oli : mapOpp.values())
        {
            If(opp.iswon == true && opp.hasopportunitylineitem ==true)
               {
                Asset a = new asset();
                a.name = a.Opportunity_Name__c;
                a.accountid = opp.Accountid;
                a.contactid = con.id;
                a.product2id = Oli.pricebookentry.product2id;
                a.quantity = Oli.quantity;
                a.price = Oli.unitprice;
                a.purchasedate = opp.closedate;
                
                assets.add(a);
                }
        }
    }

           If (assets.size()>0)
               Insert assets;
}

Please try this I have made the changes:))

let me know what happens?

Thanks
This was selected as the best answer
Adriana Smith 9Adriana Smith 9
Error:Apex trigger CreateAssetonClosedWon caused an unexpected exception, contact your administrator: CreateAssetonClosedWon: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Asset Name]: [Asset Name]: Trigger.CreateAssetonClosedWon: line 35, column 1
Anurag SaxenaAnurag Saxena
could you please ping me on skype so that I can understand your requirement correctly?

Skype ID- anurag.twopirconsulting
Adriana Smith 9Adriana Smith 9
Thank you Anurag for your help- you were amazing in helping me troubleshoot!
Adriana Smith 9Adriana Smith 9
How do I push this into production now? I'm not seeing it working :(

Deployment settings in Sandbox are done, I hit refresh in production, what am I missing?
Adriana Smith 9Adriana Smith 9
Anurag, the refresh deleted the entire trigger.  I am at a loss for words, I dont even know what to say.