Creating order from opportunity using an Apex trigger and Flow

Introduction:

In Salesforce, a common scenario is to automatically create an order from an Opportunity when the Opportunity reaches the Closed Won stage. This automation ensures a smooth transition from the sales process to order fulfillment. The following code demonstrates how to achieve this functionality using a trigger in Salesforce:

                           
  Trigger On Opportunity : CreateOrderFromOpportunity

trigger CreateOrderFromOpportunity on Opportunity (after update) {
    if(trigger.isAfter){
        if(trigger.isUpdate){
            CreateOrderFromOpportunityHandler.createOrder(Trigger.New,Trigger.oldMap);
        }
    }
}

Handler Class : CreateOrderFromOpportunityHandler

public class CreateOrderFromOpportunityHandler {

    public static void createOrder(List oppList, Map oppMap){
        Set oppIds = new Set();
        List orderList = new List();
        List orderItemList = new List();
        
        for(Opportunity opp: oppList){
            if(opp.StageName == 'Closed Won' && (oppMap.get(opp.Id).StageName != 'Closed Won')){
                oppIds.add(opp.Id);
            }
        }
        
        List oppToUpdate = [SELECT Id,HasOrder__c FROM Opportunity WHERE Id IN :oppIds];
        
        for(Opportunity opp: oppList){
            if(oppIds.contains(opp.Id)){
                Order newOrder = new Order();
                newOrder.OpportunityId = opp.Id;
                newOrder.AccountId = opp.AccountId;
                newOrder.EffectiveDate = Date.today();
                newOrder.ContractId = opp.ContractId;
                newOrder.Status = 'Draft';
                newOrder.Pricebook2Id = opp.Pricebook2Id;
                
                orderList.add(newOrder);
            }
        }
        
        if(!orderList.isEmpty()){
            insert orderList;
        }
        
        
        Map oppOrderId = new Map();
        for(Order ord: orderList){
            oppOrderId.put(ord.OpportunityId,ord.Id);
        }
        
        List oppLineItem = [SELECT Id, Product2Id, Quantity, UnitPrice, PricebookEntryId, OpportunityId FROM OpportunityLineItem WHERE OpportunityId IN :oppIds];
        
        if(!oppLineItem.isEmpty()){
            for(OpportunityLineItem oli: oppLineItem){
                OrderItem orItem = new OrderItem();
                orItem.OrderId = oppOrderId.get(oli.OpportunityId);
                orItem.Product2Id = oli.Product2Id;
                orItem.Quantity = oli.Quantity;
                orItem.UnitPrice = oli.UnitPrice;
                orItem.PricebookEntryId = oli.PricebookEntryId;
                
                orderItemList.add(orItem);
            }
            
            insert orderItemList;
        }
        
        for(opportunity opp: oppToUpdate){
            opp.HasOrder__c = true;
        }
        
        update oppToUpdate;
    }
}


Test Class
@isTest
public class CreateOrderFromOpportunityHandlerTest {

    @isTest
    public static void testOrderCreation(){
        Account acc = new Account(Name='TestAccountOrder');
        insert acc;
        
        Contract ct = new Contract(
            AccountId = acc.Id,
            Status = 'Draft',
            StartDate = date.today(),
            ContractTerm = 5
        );
        insert ct;
        
        Product2 pro = new Product2(
            Name='OppProduct', 
            Description = 'This Product is related to Opportunity', 
            IsActive = true
        );
        insert pro;
        
        Id pricebookId = Test.getStandardPricebookId();
        
        PricebookEntry pbEntry = new PricebookEntry(
            Pricebook2Id = pricebookId, 
            Product2Id = pro.Id, 
            UnitPrice = 50.0, 
            IsActive = true
        );
        insert pbEntry;
        
        Opportunity op = new Opportunity(
            AccountId = acc.Id,
            CloseDate = date.today(),
            ContractId = ct.Id,
            StageName = 'Qualification',
            Name = 'TestOppforOrder'
        );
        insert op;
        
        OpportunityLineItem oli = new OpportunityLineItem();
        oli.OpportunityId = op.Id;
        oli.Product2Id = pro.Id;
        oli.PricebookEntryId = pbEntry.Id;
        oli.Quantity = 5;
        oli.TotalPrice = 10;
        insert oli;
        
        
        Test.startTest();
        Opportunity o = [SELECT Id, Name, StageName FROM Opportunity WHERE Name='TestOppforOrder' LIMIT 1];
        o.StageName = 'Closed Won';
        update o;
        Test.stopTest();
    }
}

                           
                        

Explanation:

  • The trigger is defined on the Opportunity object and set to execute after an update.
  • The trigger iterates over the updated Opportunities and checks if their stage has changed to "Closed Won" from a previous stage that is not "Closed Won".
  • For each qualifying Opportunity, a new Order is created.
  • The necessary Opportunity field values are copied to the Order.
  • The Opportunity Line Items (products) are queried and used to create corresponding Order Items.
  • The Order Items are added to the Order.
  • The Orders are inserted into Salesforce.
  • The Opportunities associated with the newly created Orders are updated to reflect the Order information.
  • The Opportunities are updated in Salesforce.

Please note that this code is a basic implementation and may require customization to fit your specific business requirements. It is recommended to test the trigger thoroughly in a sandbox or developer org before deploying it to a production environment.

Output

Opportunity Records with  Stage equals

Figure 1:Opportunity Records with Stage equals “closed won” .

Order Created when Opportunity stage field equals

Figure 2:Opportunity Records with Stage equals “closed won” .

The next section explains how to implement this using Flows.

Creating order from opportunity using Flows

Record Triggered Flow on Opportunity

Figure 3:Record Triggered Flow on Opportunity

Record Triggered Flow on Opportunity

Figure 4:Opportunity records with Stage equal “Closed-Won”

Order created When opportunity record stage equals “Closed Won” Triggered the flow

Figure 5:Order created When opportunity record stage equals “Closed Won” Triggered the flow

Conclusion:

This automation streamlines the sales process and facilitates a smooth transition from Opportunity to order fulfillment in Salesforce. It can be customized and extended to fit specific business requirements. It's important to thoroughly test the trigger and handle any additional validation or business logic that may be necessary before deploying it to a production environment. Alternatively, if you prefer a more visual approach, the implementation can also be achieved using Flows. The provided figures demonstrate a Record-Triggered Flow on Opportunity that creates an Order when the Opportunity reaches the "Closed Won" stage. Overall, the code and flow examples serve as a foundation for automating the creation of orders from Opportunities, enhancing the efficiency and accuracy of your Salesforce sales processes.

For any queries please reach out to support@astreait.com.