Skip to main content

Copy custom fields values from Sales Quotes and Opportunities to Sales Order

  • December 11, 2024
  • 3 replies
  • 135 views

mvolshteyn
Acumatica Moderator
Forum|alt.badge.img+3
  • Technical Account Manager in the ISV Team
  • 110 replies

Use Case

It might be necessary to override the Convert To Order action in the Sales Quotes and Opportunities screens to modify a logic of copying or to copy a line custom field value from a Sales Quote or Opportunity lines to Sales Order lines.

 

Solution

Create Sales Order action is present in both Opportunities and Sales Quotes screen. And it has been separated logically to a different class CRCreateSalesOrder and is being extended and utilized from both OpportunityMaint and QuoteMaint. In this article, we will understand how CRCreateSalesOrder extension can be extended to create sales order. 

 

Overriding Copy Sales Quote code to Sales Order 

In this example, we are trying to extend CRCreateSalesOrderExt graph extension of the QuoteMaint graph in order to fill PO Source = Drop Ship in Sales Order lines where the corresponding Sales Quote line has Mark for PO checked (by default, PO Source is filled as Purchase to Order in a sales order line in this case)

The below code adds RowUpdated event handler to SOLine cache within which we are overriding the PO Source of sales order lines created in this process. 

public class QuoteMaintCRCreateSalesOrderExt : PXGraphExtension<PX.Objects.CR.QuoteMaint.CRCreateSalesOrderExt,
		QuoteMaint>
	{
		public delegate void DoCreateSalesOrderDelegate();
    [PXOverride]
 	public void DoCreateSalesOrder(DoCreateSalesOrderDelegate baseMethod)
	{
		SOOrderEntry SOGraph = PXGraph.CreateInstance<SOOrderEntry>();
		PXGraph.InstanceCreated.AddHandler(delegate (SOOrderEntry graph)
		{
			graph.RowUpdated.AddHandler<SOLine>(delegate (PXCache sender, PXRowUpdatedEventArgs e)
			{
				SOLine objSOLine = e.Row as SOLine;
				if (objSOLine != null)
					{
						foreach (CROpportunityProducts item in
								PXSelect<
									CROpportunityProducts,
								Where<
									CROpportunityProducts.quoteID, Equal<Current<PX.Objects.CR.Extensions.CRCreateSalesOrder.Document.quoteID>>,
									And<CROpportunityProducts.sortOrder, Equal<Required<CROpportunityProducts.sortOrder>>>>>
								.Select(Base, objSOLine.SortOrder))
							{
								if (item.POCreate == true)
								{
									sender.SetValueExt<SOLine.pOCreate>((object)objSOLine, true);
									sender.SetValueExt<SOLine.pOSource>((object)objSOLine, "D");
								}
						}
					}
				});

			});
			baseMethod();
		}
}

 

Copy custom fields from Opportunities (OpportunityMaint) to Sales Order 

  1. As the logic to creating a sales order from Opportunities lies in CRCreateSalesOrder and a graph extension – CRCreateSalesOrderExt which lies inside OpportunityMaint, you need to create a second-level graph extension - CRCreateSalesOrderExt_extension like this:
     

    public class CRCreateSalesOrderExt_Extension : PXGraphExtension<OpportunityMaint.CRCreateSalesOrderExt, OpportunityMaint>
    {
        [PXOverride]
        [PXUIField()]
        [PXButton]
        public virtual IEnumerable createSalesOrder(PXAdapter adapter, Func<PXAdapter, IEnumerable> baseMethod)
        {
            //return baseMethod.Invoke(adapter);
            Customer customer = PXSelect<
                    Customer,
                Where<
                    Customer.bAccountID, Equal<Current<Document.bAccountID>>>>
                .SelectSingleBound(Base, new object[] { Base1.DocumentView.Current });
            if (customer == null)
            {
                throw new PXException(Messages.ProspectNotCustomer);
            }
            if (Base1.CreateOrderParams.AskExtFullyValid((graph, viewName) => { }, DialogAnswerType.Positive))
            {
                Base.Actions.PressSave();
                //var graph = Base.CloneGraphState();
                PXLongOperation.StartOperation(Base, delegate ()
                {
                    Base1.DoCreateSalesOrder();
                });
            }
            return adapter.Get();
        }
    } 

     

  2. Add a custom field (example: UsrCustomField) by creating an extension to CROpportunity DAC.
     

    public class CROpportunityExt1 : PXCacheExtension<PX.Objects.CR.CROpportunity>
    {
        #region UsrCustomField
        [PXString]
        [PXUIField(DisplayName = "Custom Field")]
        [PXDefault("Default")]
        public virtual string UsrCustomField { get; set; }
        public abstract class usrCustomField : PX.Data.BQL.BqlString.Field<usrCustomField> { }
        #endregion
    }

     

  3. Inside the CRCreateSalesOrderExtension, we need to override the createSalesOrder action – and enclose the logic from original action to execute the creation of a sales order and add the below logic inside the PXLongOperation before actually calling the DoCreateSalesOrder method to pass the custom field to SOOrder:
     

    PXGraph.InstanceCreated.AddHandler<SOOrderEntry>(graph1 =>
    {
        graph1.RowUpdated.AddHandler<SOOrder>((cache, e) =>
        {
            //CROpportunity opportunity = new CROpportunity();
            //CROpportunity opportunity = PXSelect<CROpportunity>.SelectSingleBound(Base, new object[] { Base.Opportunity.Current });
            CROpportunityExt1 oppExt = PXCache<CROpportunity>.GetExtension<CROpportunityExt1>(Base.Opportunity.Current);
            string oppur = oppExt.UsrCustomField;
            SOOrder soorder = (SOOrder)e.Row;
            //soorder.OrderDesc += " test ";
            if (soorder != null)
            {
                if (soorder.OrderDesc != null)
                {
                    if (!soorder.OrderDesc.ToString().Contains(oppur))
                        soorder.OrderDesc += oppur;
                    // add logic here
                }
            }
        });
    });

    The above logic will add RowUpdated event to SOOrder within which we are accessing the custom field from the Current object and append it to the Description of a Sales Order as an example

Did this topic help you find an answer to your question?

3 replies

dans97
Freshman I
  • Freshman I
  • 2 replies
  • December 18, 2024

When copying quote lines to order lines, you are getting the CROpportunityProducts line by selecting the SOLine with the same InventoryID. How would you do this if there were multiple lines with the same InventoryID? As far as I can tell, there is no good way to link the SOLine to the CROpportunityProducts line with the new method of creating sales orders.


mvolshteyn
Acumatica Moderator
Forum|alt.badge.img+3
  • Author
  • Technical Account Manager in the ISV Team
  • 110 replies
  • January 1, 2025

@dans97 , I updated the code(in the Overriding Copy Sales Quote code to Sales Order  part) to  address your question. The updated code searches for the CROpportunityProducts with the same SortOrder (a unique field within a single document) as the current SOLine.  


dans97
Freshman I
  • Freshman I
  • 2 replies
  • January 7, 2025

Thanks Mark!


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings