0

Scenario:

When an Opportunity is closed/won, we are creating an Order record with the corresponding OrderItem records based on the OpportunityLineItems of the Opportunity. When the OrderItems are created, we want to trigger Assets to be created on the Account. We currently have an integration with NetSuite where we send the Opportunity & OpportunityLineItem data over to NetSuite, they create a NetSuite Sales Order, and then communicates back to Salesforce to create the Salesforce Order record with the OrderItems as well.

Trigger Code:

trigger OrderLineItem_AI_TRG on OrderItem (before insert, before update, after insert) {
if(Trigger.isBefore){
    for(OrderItem lineItemObj:Trigger.new){
        String oppId = [Select OpportunityId From Order Where Id = :lineItemObj.OrderId].OpportunityId;
        for(OpportunityLineItem oppItem: [Select Id, Discount, League_LP_new__c, Per_Item_Fee__c, Upfront__c, Term__c, Fee__c, Registration_Flat_Fee__c, Size__c, Is_Flat_Fee__c, cc_Rate__c, CC_Price__c, BSS_Price__c, Description From OpportunityLineItem Where Product2Id = :lineItemObj.Product2Id and OpportunityId = :oppId and Opportunity.StageName = 'Closed Won' Order By CreatedDate DESC limit 1]){
            lineItemObj.Discount__c = oppItem.Discount != null ? oppItem.Discount : null;
            lineItemObj.League_LP__c = oppItem.League_LP_new__c;
            lineItemObj.Per_Item_Fee__c = oppItem.Per_Item_Fee__c;
            lineItemObj.Upfront__c = oppItem.Upfront__c;
            lineItemObj.Term__c = oppItem.Term__c;
            lineItemObj.Fee__c = oppItem.Fee__c;
            lineItemObj.Registration_Flat_Fee__c = oppItem.Registration_Flat_Fee__c;
            lineItemObj.Size__c = oppItem.Size__c;
            lineItemObj.Is_Flat_Fee__c = oppItem.Is_Flat_Fee__c;
            lineItemObj.CC_Rate__c = oppItem.cc_Rate__c;
            lineItemObj.CC_Price__c = oppItem.CC_Price__c;
            lineItemObj.BSS_Price__c = oppItem.BSS_Price__c;
            lineItemObj.Description = oppItem.Description;
        }
    }
}

if(Trigger.isInsert && Trigger.isAfter){
    for(OrderItem lineItemObj:Trigger.new){
        if(Recursivehandler.runOnce()){
            String orderObjId = lineItemObj.OrderId;
            OrderItem[] OLI = [Select Order.Account.Id, Product2Id, Product2.Name, Quantity, UnitPrice, Order.Opportunity.CloseDate, Order.Opportunity.Contract_End_Date__c, Discount__c, League_LP__c, Per_Item_Fee__c, Upfront__c, Term__c, Fee__c, Registration_Flat_Fee__c, Size__c, Is_Flat_Fee__C, CC_Rate__c, CC_Price__c, BSS_Price__c, Description/*, Converted_to_Asset__c*/ From OrderItem Where OrderId = :orderObjId/* and Converted_to_Asset__c = false*/];
            Asset[] ast = new Asset[]{};
            Asset a = new Asset();
            for(OrderItem oi: OLI){
                a = new Asset();
                a.AccountId = oi.Order.Account.Id;
                a.Product2Id = oi.Product2Id;
                a.Name = oi.Product2.Name;
                a.Quantity = oi.Quantity;
                a.Price__c = oi.UnitPrice;
                a.PurchaseDate = oi.Order.Opportunity.CloseDate;
                a.UsageEndDate = oi.Order.Opportunity.Contract_End_Date__c;
                a.Status = 'Active';
                a.Discount__c = oi.Discount__c;
                a.League_LP__c = oi.League_LP__c;
                a.Per_Item_Fee__c = oi.Per_Item_Fee__c;
                a.Upfront__c = oi.Upfront__c;
                a.Term__c = oi.Term__c;
                a.Fee__c = oi.Fee__c;
                a.Registration_Flat_Fee__c = oi.Registration_Flat_Fee__c;
                a.Size__c = oi.Size__c;
                a.Is_Flat_Fee__c = oi.Is_Flat_Fee__c;
                a.CC_Rate__c = oi.CC_Rate__c;
                a.CC_Price__c = oi.CC_Price__c;
                a.BSS_Price__c = oi.BSS_Price__c;
                a.Description = oi.Description;
                ast.add(a);
            }
            insert ast;
        }
    }
}}

Issue:

The trigger is not finding the Product2Id in the SOQL query within the "isBefore" portion of the code. This results in the OpportunityLineItem field data not being added to the Asset being created within the "isAfter" portion of the code. The Assets are being created, it's just not adding in that OpportunityLineItem data.

1
  • you have bulkification issues here with queries inside of for loops
    – cropredy
    Commented Oct 3, 2017 at 21:34

3 Answers 3

1

I don't see where the product2ID field is being populated but since you're not populating any information into the record until it's created, it's likely that there is no ID meaning your code will never run.

I think you might be trying to do too much in one trigger. The order item should already have all the information it needs to create the asset by the time it's trigger is called. Your trigger should literally just create the asset and populate it with the necessary data.

To populate the orderItem with data you should use formula fields since you're taking most of the information from a corresponding opp item. Just create a field to reference that opp item and use formulas to pull in the data. Otherwise, when the opp status changes to closed won, you create the order, when the order is created, you go to the opp to get opp items to create the order items and when the order items are created you create the assets. I think if you split up your logic you'll find it'll work much better, or at the very least, be easier to troubleshoot issues and modify how it all works.

You're query for the Opp ID should also be bulkified...

Set<Id> orderIds = new Set<Id>();

for (OrderItem lineItemObj : Trigger.new) {
    orderIds.add(lineItemObj.OrderId);
}

Map<Id, Order> orders = new Map<Id, Order>(
    SELECT OpportunityId
    FROM Order
    WHERE Id IN :orderIds
);

for (OrderItem lineItemObj : Trigger.new) {
    OppId = orders.get(lineItemObj.orderId).OpportunityId;
}

In fact, you have several queries inside for loops which is a big no-no.

3
  • The code is in our sandbox and I have not gone through the process to bulkify it yet. I'm fairly new to apex coding, so I wanted to get this trigger working before bulkifying it. Thanks for the reminder though!
    – Brandon
    Commented Oct 3, 2017 at 21:48
  • The OrderItem won't have all of the OpportunityLineItem information because of our workflow and how the OrderItems are being created. This is the process.... Opportunity is closed/won > Select amount of data sent to NetSuite (3rd party software) > NetSuite creates a NetSuite Sales Order > NetSuite sends the NetSuite Sales Order info back to Salesforce > Salesforce Order is created with OrderItems > this Triggers the Assets to be created. In this process, only a small amount of basic data ends up making its way to the Order and OrderItems. Let me know if you need more clarification.
    – Brandon
    Commented Oct 3, 2017 at 21:52
  • Have you contacted NetSuite about this issue?
    – gNerb
    Commented Oct 4, 2017 at 20:59
0

Thanks for all of the help, we were able to get it functioning with the following trigger code thanks to everyone's input along with some help from a co-worker...

trigger OrderLineItem_AI_TRG on OrderItem (before insert, before update, after insert) {

Set<Id> orderIds = new Set<Id>();
if(Trigger.isBefore){
    for(OrderItem lineItemObj : Trigger.new){
        if(lineItemObj.OrderId != null){
            orderIds.add(lineItemObj.OrderId);
        }
    }

    Set<Id> oppId = new Set<Id>();
    for(Order odr : [Select OpportunityId From Order Where Id In :orderIds]){
        if(odr.OpportunityId != null){
            oppId.add(odr.OpportunityId);           
        }
    }

    List<OpportunityLineItem> oliList=[Select Id, Discount, League_LP_new__c, Per_Item_Fee__c, Upfront__c, Term__c, Fee__c, Registration_Flat_Fee__c, Size__c, Is_Flat_Fee__c, cc_Rate__c, CC_Price__c, BSS_Price__c, Description, PriceBookEntryId From OpportunityLineItem Where OpportunityId In :oppId and Opportunity.StageName = 'Closed Won' and CreatedDate = Last_n_Days:45 Order By CreatedDate DESC];
    if(oliList.size()>0){
        for(OrderItem lineItemObj : Trigger.new){
            for(OpportunityLineItem oppItem: oliList){
                if(oppItem.PriceBookEntryId == lineItemObj.PriceBookEntryId){
                    lineItemObj.Discount__c = oppItem.Discount != null ? oppItem.Discount : null;
                    lineItemObj.League_LP__c = oppItem.League_LP_new__c;
                    lineItemObj.Per_Item_Fee__c = oppItem.Per_Item_Fee__c;
                    lineItemObj.Upfront__c = oppItem.Upfront__c;
                    lineItemObj.Term__c = oppItem.Term__c;
                    lineItemObj.Fee__c = oppItem.Fee__c;
                    lineItemObj.Registration_Flat_Fee__c = oppItem.Registration_Flat_Fee__c;
                    lineItemObj.Size__c = oppItem.Size__c;
                    lineItemObj.Is_Flat_Fee__c = oppItem.Is_Flat_Fee__c;
                    lineItemObj.CC_Rate__c = oppItem.cc_Rate__c;
                    lineItemObj.CC_Price__c = oppItem.CC_Price__c;
                    lineItemObj.BSS_Price__c = oppItem.BSS_Price__c;
                    lineItemObj.Description = oppItem.Description;
                }
            }
        }
    }
}

if(Trigger.isInsert && Trigger.isAfter){
    Set<Id> orderItemIds = new Set<Id>();
    for(OrderItem lineItemObj : Trigger.new){
        if(lineItemObj.OrderId != null){
            orderItemIds.add(lineItemObj.Id);
        }
    }

    List<OrderItem> OLI = [Select Order.AccountId, Product2Id, Product2.Name, Quantity, UnitPrice, Order.Opportunity.CloseDate, Order.Opportunity.Contract_End_Date__c, Discount__c, League_LP__c, Per_Item_Fee__c, Upfront__c, Term__c, Fee__c, Registration_Flat_Fee__c, Size__c, Is_Flat_Fee__C, CC_Rate__c, CC_Price__c, BSS_Price__c, Description From OrderItem Where Id In :orderItemIds];

    Set<Id> accountIds = new Set<Id>();
    Set<Id> product2Ids = new Set<Id>();
    for(OrderItem lineItemObj: OLI){
        accountIds.add(lineItemObj.Order.AccountId);
        product2Ids.add(lineItemObj.Product2Id);
    }

    List<Asset> assetList = [Select Id, AccountId, Product2Id From Asset Where AccountId In :accountIds and Product2Id In :product2Ids];
    List<Asset> assertListForUpdate = new List<Asset>();
    List<Asset> assertListForInsert = new List<Asset>();

    for(OrderItem lineItemObj: OLI){
        Asset a = new Asset();
            a.AccountId = lineItemObj.Order.AccountId;
            a.Product2Id = lineItemObj.Product2Id;
            a.Name = lineItemObj.Product2.Name;
            a.Quantity = lineItemObj.Quantity;
            a.Price__c = lineItemObj.UnitPrice;
            a.PurchaseDate = lineItemObj.Order.Opportunity.CloseDate;
            a.UsageEndDate = lineItemObj.Order.Opportunity.Contract_End_Date__c;
            a.Status = 'Active';
            a.Discount__c = lineItemObj.Discount__c;
            a.League_LP__c = lineItemObj.League_LP__c;
            a.Per_Item_Fee__c = lineItemObj.Per_Item_Fee__c;
            a.Upfront__c = lineItemObj.Upfront__c;
            a.Term__c = lineItemObj.Term__c;
            a.Fee__c = lineItemObj.Fee__c;
            a.Registration_Flat_Fee__c = lineItemObj.Registration_Flat_Fee__c;
            a.Size__c = lineItemObj.Size__c;
            a.Is_Flat_Fee__c = lineItemObj.Is_Flat_Fee__c;
            a.CC_Rate__c = lineItemObj.CC_Rate__c;
            a.CC_Price__c = lineItemObj.CC_Price__c;
            a.BSS_Price__c = lineItemObj.BSS_Price__c;
            a.Description = lineItemObj.Description;
        Boolean assetExists = false;
        for(Asset assetRec : assetList){
            if(a.AccountId == assetRec.AccountId && a.Product2Id == assetRec.Product2Id){
                a.Id = assetRec.Id;
                assetExists = true;
            }
        }
        if(assetExists){
            assertListForUpdate.add(a);
        } else{
            assertListForInsert.add(a);
        }
    }

    System.debug(assertListForInsert.Size()+' '+assertListForUpdate.Size());
    insert assertListForInsert;
    update assertListForUpdate;
}}

I'm having trouble creating a test class for this trigger though. Again, I'm very new to this and still learning. I'm specifically having issues trying to figure out how to create the Pricebook, Product, and PricebookEntry records that will be used in the Opportunity and Order/OrderItems for the test. Any help would be much appreciated!

-2

Please try this piece of code for Trigger.IsBefore. Hope it will help you.

Set<Id> orderItemId = new Set<Id>();
if(Trigger.isBefore){
    for(OrderItem lineItemObj : Trigger.new){
        if(lineItemObj.OrderId != null){
            orderItemId.add(lineItemObj.OrderId);
        }
    }
    Set<Id> oppId = new Set<Id>();
    for(Order odr : [Select OpportunityId From Order Where Id In :orderItemId]{
        if(odr.OpportunityId != null){
            oppId.add(odr.OpportunityId);           
        }
    }
    List<OpportunityLineItem> oliList=[Select Id, Discount, League_LP_new__c, Per_Item_Fee__c, Upfront__c, Term__c, Fee__c, Registration_Flat_Fee__c, Size__c, Is_Flat_Fee__c, cc_Rate__c, CC_Price__c, BSS_Price__c, Description From OpportunityLineItem Where OpportunityId In :oppId and Opportunity.StageName = 'Closed Won' Order By CreatedDate DESC limit 1];
    if(oliList.size()>0){
        for(OrderItem lineItemObj : Trigger.new){
            for(OpportunityLineItem oppItem: oliList){
                lineItemObj.Discount__c = oppItem.Discount != null ? oppItem.Discount : null;
                lineItemObj.League_LP__c = oppItem.League_LP_new__c;
                lineItemObj.Per_Item_Fee__c = oppItem.Per_Item_Fee__c;
                lineItemObj.Upfront__c = oppItem.Upfront__c;
                lineItemObj.Term__c = oppItem.Term__c;
                lineItemObj.Fee__c = oppItem.Fee__c;
                lineItemObj.Registration_Flat_Fee__c = oppItem.Registration_Flat_Fee__c;
                lineItemObj.Size__c = oppItem.Size__c;
                lineItemObj.Is_Flat_Fee__c = oppItem.Is_Flat_Fee__c;
                lineItemObj.CC_Rate__c = oppItem.cc_Rate__c;
                lineItemObj.CC_Price__c = oppItem.CC_Price__c;
                lineItemObj.BSS_Price__c = oppItem.BSS_Price__c;
                lineItemObj.Description = oppItem.Description;
            }
        }
    }
}

Regards, Akshay

1
  • Can you add some explanation as to how and why? Code on it's own doesn't always make for the best answer!
    – Matt Lacey
    Commented Oct 6, 2017 at 10:13

You must log in to answer this question.

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