[SalesForce] Salesforce trigger Opportunity and Opportunity product

I am working on a requirement to create an opportunity and opportunity products , when there is an update on the asset object.

The asset object has a filed called Refresh_Asset_To__c, this is a lookup to the product object.

Basically each time I change the product on the Refresh_Asset_To__c, a new opportunity should be generated with the Refresh_Asset_To__c being the opportunity product.

I have tested , and its generating the opportunity , but each time i try to insert the opportunity product I get the error :

Update failed. First exception on row 0 with id 02i3B000003TAk7QAG; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, AssetRefreshOpp: execution of AfterUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, field integrity exception: PricebookEntryId (pricebook entry currency code does not match opportunity currency code): [PricebookEntryId] Trigger.AssetRefreshOpp: line 56, column 1: []

This is my code:

trigger AssetRefreshOpp on Asset (after update) {
     Pricebook2 ikmBook    = [SELECT Id,CurrencyIsoCode FROM Pricebook2 WHERE Id = '01s46000003K1zXAAS'];

List<OpportunityLineItem> oppLiList = new List<OpportunityLineItem>();
Opportunity opp;
OpportunityLineItem oli;
string refeshed;

   List<asset> refreshedAss = [select Id,Contract__r.EndDate,OwnerId,quantity,Name,Refresh_Asset_To__c,Refresh_Asset_To__r.Id,AccountId,account.name from asset 
                                    where Id IN : Trigger.new 
                                    And Refresh_Asset__c = True];


       for(asset myAss:refreshedAss){


          // Create New Opportunity

            Opportunity newopp =new Opportunity();


newopp.Name=myAss.account.name + ' ' + 'refreshed';
newOpp.AccountId = myAss.AccountId;
newOpp.CloseDate = myAss.Contract__r.EndDate;
newOpp.StageName = 'Create';
newOpp.RecordTypeID = '01246000000TfMZAA0';
newOpp.OwnerId = myAss.OwnerId;
newOpp.Amount = 0;
newOpp.Type= 'Existing Business';
newOpp.OM_Order_Type__c = 'Regular';
newOpp.CurrencyIsoCode = ikmBook.CurrencyIsoCode;
newOpp.Pricebook2Id = ikmBook.Id;

insert newOpp;


       List<PriceBookEntry> priceBookList = [SELECT Id, Product2Id,CurrencyIsoCode, Product2.Id, Product2.Name FROM PriceBookEntry WHERE  PriceBook2.isStandard=true LIMIT 1];


            for(asset myAssets:refreshedAss){

             OpportunityLineItem oli = new OpportunityLineItem();

              oli.OpportunityId = newOpp.Id;

              oli.Product2Id = myAssets.Refresh_Asset_To__r.Id;

              oli.PriceBookEntryId = priceBookList[0].Id;

              oli.UnitPrice = 0;
              oli.Quantity = myAssets.Quantity;
              oppLiList.add(oli);

         }

         insert oppLiList;         

    }
} 

Best Answer

Unless the hard-coded PriceBook in your second line is the same as the Standard Price Book, this is going to fail. All the products in an Opportunity must be from the same Price Book, and you're querying a price book entry record from a price book that might not be the same as the hard-coded one.

Also, I don't think you need to specify the OLI Product2 field if you're specifying the PBE field. Salesforce will do that for you. As long as the priceBookList[0] record is the same product as the asset refers to (and I don't think you can guarantee that in your code) then you're fine. But as I see it, this is risky code. Simplify, and many of your problems will fall away.