Creating Sales Order through WebServices in E9

Hi people, I am trying to create a Sales Order through WebServices in E9.05.702. I was able to connect successfully. Here is my code so far:

private void button1_Click(object sender, EventArgs e)
 {
         try
         {
                Session objSession = null;
                objSession = new Session("manager", "manager", "AppServerDC://127.0.0.1:9431", Session.LicenseType.Default);"

                MessageBox.Show("Connection established successfully" + objSession.SessionID + " to Instance "+ objSession.CompanyName);

SalesOrder so = new SalesOrder(objSession.ConnectionPool);
SalesOrderDataSet ds = new SalesOrderDataSet();

//Here, I am stuck. Further implementation here.

System.Threading.Thread.Sleep(10000);
                ds.Dispose();
                objSession.Dispose();
                MessageBox.Show("Sesion has been disposed");
         }
catch (Exception error)
            {
                MessageBox.Show(error.Message);
                if (objSession != null)
                {
                    objSession.Dispose();
                    objSession = null;
                    MessageBox.Show("Sesion has been disposed");
                }
            }
}

The spot where I left a comment Further implementation I want to continue my developing. Here are my thoughts about the next step:

When I go ‘so.’ Intellisense in VS shows me a bunch of methods that I can use. As per ‘Create’ I can see:
image

None of them are going to work for me as there will be no Quotes, so I can’t do that. The idea here is to map all the necessary fields from XML document and submit them into E9.

There are some methods related to OrderHed, Dtl, etc. For example:

Should I be looking for those might be? Like GetNewOrderHed. It looks like it accepts an instance of SalesOrderDataSet but I can’t populate it with fields in order to send it to Epicor. I might be wrong here though.

Any ideas and thoughts would be very much appreciated.

Thank you,

Alex

Run a trace in Epicor. You will see the methods that are called. Yes, next step is GetNewOrderHed. You would update fields in the dataset.

1 Like

Hi @Jason_Woods. Thank you for your input. I have tried to run a TraceLog with (WriteDataSet option on) and I see what do you mean by methods that are called. However, I think I confused myself and confused everybody and here is why. The way I was connecting to Epicor is by grabbing necessary dlls and adding them to my project so I would have access to those and Intellisense. I actually need to connect to Epicor through WebServices first I suppose and then try to create Sales Order.

I updated my code so it would match the approach I need to take I guess.

private void button1_Click(object sender, EventArgs e)
{
 try {
       SalesOrderService.SalesOrderServiceSoapClient client = new SalesOrderService.SalesOrderServiceSoapClient();
                
       client.ClientCredentials.UserName.UserName = "manager";
       client.ClientCredentials.UserName.Password = "manager";
                
       SalesOrderService.SalesOrderDataSetType dss = new SalesOrderService.SalesOrderDataSetType();
       SalesOrderService.CallContextDataSetType callContextIn = new SalesOrderService.CallContextDataSetType();
       SalesOrderService.CallContextDataSetType callContextOut = new SalesOrderService.CallContextDataSetType();
       SalesOrderService.SalesOrderDataSetTypeOrderHed orderHedReq = new SalesOrderService.SalesOrderDataSetTypeOrderHed();

       orderHedReq.OrderNum = 10002;
       orderHedReq.CustNum = 1;
       var response = client.GetNewOrderHed("TEST", dss, callContextIn, out callContextOut);
       
}
}

This code fails on client.GetNewOrderHed call and returns the next error:
image

and I do not see how I can pass a security header in addition to UserName and Password.

What do you think?

Alex

P.S. Might be @josecgomez will have a clue what’s going on?

You need to read the web services guide available in EpicWeb

1 Like

Hey Jose,

Thank you for a pointer. I looked through that documentation. I found this example and tried to do a similar thing but for SalesOrder.

However, I do not have SalesOrderServiceWse or SalesOrderServiceClient class definitions in order to instantiate a proper proxy class. The only one I have is SalesOrderServiceSoapClient that more or less make sense and I’ve seen how it’s usually used to consume Web Services.

I also found this example:


As I can see, they do not use any Tokens in this one, but just Username and Password unless, they forgot to put a piece with Token code. This is kinda confusing. My question is where would I be able to find a proper proxy class to instantiate?

If you consume the WSLD as a Web Service Reference and not a “Service Reference” it will generate correctrly
You also have to use the WSE runtime, again all this is in the guide step by step.

Hi Jose,

By saying “You also have to use the WSE runtime” do you mean “Enable this project for Web Services Enhancements” or something else? The guide is showing that you need to go and select WSE Settings 3.0 button on a project file and then go and add a Web Reference. I was able to add Web Service in VS2017 without enabling WSE Settings in advance. Moreover, I do not even have that option when I right-click on a project file. Just wondering if I am missing a step here or it’s all good. Also, I verified that WSE Runtime is installed.

So, after I added WSDL as Web Service Reference I got SalesOrderService class generated for me. I followed all the steps from the guide and now I am having a problem with SetPolicy method it says that SalesOrderService doesn’t contain a definition of SetPolicy. Does it mean that I have to call SetPolicy method in a different way or might be using the wrong object?
Here is my updated code:

try
            {
                SalesOrderReferenceE9.SalesOrderService proxy = new SalesOrderReferenceE9.SalesOrderService();
                Trace.WriteLine("Line 1 -----------------");
                SalesOrderReferenceE9.SalesOrderDataSetType ds = new SalesOrderReferenceE9.SalesOrderDataSetType();
                Trace.WriteLine("Line 2 -----------------");
                SalesOrderReferenceE9.CallContextDataSetType callContextIn = new SalesOrderReferenceE9.CallContextDataSetType();
                Trace.WriteLine("Line 3 -----------------");
                SalesOrderReferenceE9.CallContextDataSetType callContextOut = new SalesOrderReferenceE9.CallContextDataSetType();
                Trace.WriteLine("Line 4 -----------------");
                
                UsernameOverTransportAssertion userNameAssertion = new UsernameOverTransportAssertion();
                Trace.WriteLine("Line 5 -----------------");
                userNameAssertion.UsernameTokenProvider = new UsernameTokenProvider("manager", "manager");
                Trace.WriteLine("Line 6 -----------------");

                Policy policy = new Policy();
                policy.Assertions.Add(userNameAssertion);
                Trace.WriteLine("Line 7 -----------------");
                proxy.SetPolicy(policy);
                
                
                Trace.WriteLine("Line 8 -----------------");
                SalesOrderReferenceE9.SalesOrderDataSetType soDataSet = proxy.GetNewOrderHed("TEST", ds, callContextIn, out callContextOut);
}

P.S. Should I be modifying my previous posts to keep this thread clear? or a new Reply is fine?

As stated in the guide you need to install and use
https://www.microsoft.com/en-us/download/details.aspx?id=14089

Because you are in 9
And when you add the Service you need to add it as a WebReference not a Service Reference (Advanced Options)

1 Like

Update. After pocking around I was able to bring SetPolicy to SalesOrderService class by changing the inherited class from System.Web.Services.Protocols.SoapHttpClientProtocol to Microsoft.Web.Services3.WebServicesClientProtocol. I guess the actual problem was that I got it right at first but then re-imported WSDL and forgot to change it again because re-importing will re-write your changes.

Thanks Jose, I am moving forward!=)

1 Like

Hey @josecgomez, I think I almost was able to post the entire order into Epicor but, it looks like it doesn’t want to process a Detail level information and returns me this error:

image

At this point, Header was populated successfully but, none of the Details. I think that it might be related to OrderNum on Detail level being passed as 0 but, I do not see how do I specify a Number of a new order that hasn’t been created yet. Unless I have to do two separate calls, one for Header and one for Detail which I did not find available methods to call. Here is how my code looks like:

SalesOrderDataSetType ds = new SalesOrderDataSetType();

SalesOrderDataSetTypeOrderHed soDSHed = new SalesOrderDataSetTypeOrderHed();

ds = soService.GetNewOrderHed("TEST", ds, callContextIn, out callContextOut);
soDSHed.Company = "TEST";
soDSHed.OrderNum = 0;
soDSHed.CustNum = 1;
soDSHed.CustomerCustID = "TEST";
soDSHed.ShipViaCode = "UPSG";
                
SalesOrderDataSetTypeOrderDtl soDSDtl = new SalesOrderDataSetTypeOrderDtl();
//ds = soService.GetNewOrderDtl("TEST", ds, 0, callContextIn, out callContextOut);//This guy is commented out as it won't accept 0 as OrderNum and would return an error that valid OrderNum is required.
soDSDtl.Company = "TEST";
soDSDtl.OrderNum = 0;
soDSDtl.OrderLine = 1;
soDSDtl.PartNum = "6.WD8";
soDSDtl.CustNum = 1;
soDSDtl.SellingQuantity = 2.0m;
                
ds.SalesOrderDataSet[0] = soDSHed;
Trace.WriteLine("Line 9 -----------------");
ds.SalesOrderDataSet[1] = soDSDtl;
Trace.WriteLine("Line 10 -----------------");

soService.SubmitNewOrder("TEST", ds, callContextIn, out callContextOut);

Trace.WriteLine("Line 11 -----------------");

I confirmed that all the information exists in Epicor and I can create a new order Header and Detail with all those values from UI.

Any thoughts or advice would be very much appreciated. Thank you.

GetNewOrderHed method only you able to insert header details. for line level you have to invoke anther method

Hi @surendrapal, for the line-level, apparently GetNewOrderDtl would be suitable but, it looks like I can’t call it as it expects a valid OrderNum as one of the parameters and at that time Order has not been created yet so there is no corresponding OrderNum that I can specify. Look at my code example, that method is commented out and there is a comment.

SalesOrderDataSetTypeOrderDtl soDSDtl = new SalesOrderDataSetTypeOrderDtl();
//ds = soService.GetNewOrderDtl("TEST", ds, 0, callContextIn, out callContextOut);//This guy is commented out as it won't accept 0 as OrderNum and would return an error that valid OrderNum is required.
soDSDtl.Company = "TEST";
soDSDtl.OrderNum = 0;
soDSDtl.OrderLine = 1;
soDSDtl.PartNum = "6.WD8";
soDSDtl.CustNum = 1;
soDSDtl.SellingQuantity = 2.0m;

once you save header detail you will have order number use that one.

Hi @surendrapal, that approach won’t work. I tried to do it in that way but it will create a header first and then will create another header with the same info along with detail(s).

However, I found a specific method that actually speaks for itself. It’s called SubmitNewOrder it accepts an array of objects like OrderHed, OrderDtl, OrderRel, OrderRelTax, OrderHedMsc and others that might be related to an order. I tried to use that method but when I am trying to submit a new order with populated OrderHed, OrderDtl, OrderRel objects it will return Object reference is not set to an instance of an object error and you can’t do anything about it. I tried different approaches and ways, tried to populate as many fields as I can but no luck. All the data I was using is legit and exists and available in Epicor. I suspect that WebServices API might’ve not been finished or broken or I am missing something but after spending weeks on this particular problem I just do not see what’s wrong.

here is a webservice function in .NET that creates sales order header and details.

Might help, might not.

 private Boolean ProcessOrder(SalesOrderTransfer NewOrder)
        {
            //Create Epicor Link
            //Process the Orderfile
            try
            {
                using (var session = GetSessionID())
            {
            
                using (var OrderProxy = GetOrder(session))
                {
                    Erp.BO.SalesOrderDataSet sods = new Erp.BO.SalesOrderDataSet();

                    //When we have more customers we will need to set the vaule differently???
                    bool blnOut;
                    string strResponseMsg;
                    string strCreditShipAction;
                    string strDisplayMsg;
                    string strCompliantMsg;
                    string strResponseMsgOrdRel;

                    /*This sets the defaults for each customer*/
                    var identity = (ClaimsIdentity)User.Identity;
                    Claim claimCustNum = identity?.FindFirst("CustomerNum");
                    Claim claimCustId = identity?.FindFirst("CustomerID");
                    Claim claimDefaultFOB = identity?.FindFirst("DefaultFOB");
                    Claim claimTermsCode = identity?.FindFirst("TermsCode");
                                        
                    string strCustId = claimCustId.Value;
                    int intCustomerNum = int.Parse(claimCustNum.Value);
                    string strDefaultFOB = claimDefaultFOB.Value;
                    string strTermsCode = claimTermsCode.Value;


                    int intSalesOrder = 0;
                    int intDetailCount = 0;
                    
                    foreach (var salesorder in NewOrder.SalesOrderCreate)
                    {

                        //if (salesorder.OrderHeader.NeedByDate.ToString() == "") break;
                        //We first need to create the OrderHeader
                        //this should only be done once per file
                        if (intSalesOrder == 0)
                        { 
                            //Create New Order
                            OrderProxy.GetNewOrderHed(sods);

                            intSalesOrder = sods.OrderHed[0].OrderNum;
                            //Default Fields Created
                            sods.OrderHed[0].Company = "HI";
                            sods.OrderHed[0].CustNum = intCustomerNum;
                            sods.OrderHed[0].CustomerCustID = strCustId;
                            sods.OrderHed[0].BTCustNum = intCustomerNum;
                            sods.OrderHed[0].ShipToCustNum = intCustomerNum;
                            OrderProxy.ChangeSoldToID(sods);
                            sods.OrderHed[0].FOB = strDefaultFOB;
                            sods.OrderHed[0].OrderDate = DateTime.Now;
                            sods.OrderHed[0].ShipViaCode = salesorder.OrderHeader.ShipViaCode.ToString();  //"FX1";
                            sods.OrderHed[0].TermsCode = strTermsCode;
                            sods.OrderHed[0].UseOTS = true;
                            
							OrderProxy.ChangeHedUseOTS(sods);
							sods.OrderHed[0]["Date01"] = DateTime.Now;


                            //Fields from first record of file
                            //DateTime? myDateTime =  DateTime.ParseExact(salesorder.OrderHeader.NeedByDate.ToString(), "MM/dd/yyyy", null);

                            if (salesorder.OrderHeader.NeedByDate.ToString() == "")
                            {
                                    sods.OrderHed[0].NeedByDate = Convert.ToDateTime("01/01/2010");
                            }
                            else
                            {
                                    sods.OrderHed[0].NeedByDate = Convert.ToDateTime(salesorder.OrderHeader.NeedByDate.ToString());
                            }

                            OrderProxy.ChangeNeedByDate(sods, "OrderHed");

                            sods.OrderHed[0].OrderComment = salesorder.OrderHeader.OrderComment.ToString();
                            sods.OrderHed[0].ShipComment = salesorder.OrderHeader.ShipComment.ToString();
                            sods.OrderHed[0].OTSAddress1 = salesorder.OrderHeader.OTSAddress1.ToString();
                            sods.OrderHed[0].OTSAddress2 = salesorder.OrderHeader.OTSAddress2.ToString();
                            sods.OrderHed[0].OTSAddress3 = salesorder.OrderHeader.OTSAddress3.ToString();
                            sods.OrderHed[0].OTSCity = salesorder.OrderHeader.OTSCity.ToString();
                            sods.OrderHed[0].OTSContact = salesorder.OrderHeader.OTSContact.ToString();
                            sods.OrderHed[0].OTSCountryNum = int.Parse(salesorder.OrderHeader.OTSCountryNum.ToString());
                            //sods.OrderHed[0].OTSFaxNum = row["OTSFaxNum"].ToString();
                            sods.OrderHed[0].OTSName = salesorder.OrderHeader.OTSName.ToString();
                            sods.OrderHed[0].OTSPhoneNum = salesorder.OrderHeader.OTSPhoneNum.ToString();
                            //sods.OrderHed[0].OTSResaleID = row["OTSResaleID"].ToString();
                            sods.OrderHed[0].OTSState = salesorder.OrderHeader.OTSState.ToString();
                            sods.OrderHed[0].OTSZIP = salesorder.OrderHeader.OTSZIP.ToString();
                            sods.OrderHed[0].PONum = salesorder.OrderHeader.PONum.ToString();
                            sods.OrderHed[0].RequestDate = Convert.ToDateTime(salesorder.OrderHeader.RequestDate.ToString());
                            sods.OrderHed[0]["ShortChar02"] = salesorder.OrderHeader.CustomerPONum.ToString();
                            sods.OrderHed[0]["ShortChar03"] = salesorder.OrderHeader.CustomerCustId.ToString();
                            sods.OrderHed[0]["CustRef_c"] = salesorder.OrderHeader.CustomerRefInfo.ToString();
                            sods.OrderHed[0]["NeedTrackingSent_c"] = 1;
                            sods.OrderHed[0]["OrderContact_c"] = salesorder.OrderHeader.OTSContact.ToString();


                                OrderProxy.MasterUpdate(true, true, "Orderhed", intCustomerNum, intSalesOrder, true, out blnOut, out strResponseMsg, out strCreditShipAction, out strDisplayMsg, out strCompliantMsg, out strResponseMsgOrdRel, sods);

                            intSalesOrder = sods.OrderHed[0].OrderNum;
                        }

                        foreach (var line in salesorder.LineItems)
                        { 
                            //Create new Lines for the order
                            OrderProxy.GetNewOrderDtl(sods, intSalesOrder);
                            intDetailCount = sods.OrderDtl.Count - 1;

                            //sods.OrderDtl[intDetailCount].PartNum = line.PartNum.ToString();
                            //sods.OrderDtl[intDetailCount].PartNumPartDescription = line.PartNum.ToString();

                            OrderProxy.ChangePartNum(sods, false, "");

                            if (salesorder.OrderHeader.NeedByDate.ToString() == "")
                            {
                                sods.OrderDtl[intDetailCount].NeedByDate = Convert.ToDateTime("01/01/2010");
                            }
                            else
                            {
                                sods.OrderDtl[intDetailCount].NeedByDate = Convert.ToDateTime(salesorder.OrderHeader.NeedByDate.ToString());
                            }

                            OrderProxy.ChangeNeedByDate(sods, "OrderDtl");

                            sods.OrderDtl[intDetailCount].RequestDate = Convert.ToDateTime(salesorder.OrderHeader.RequestDate.ToString());
                            sods.OrderDtl[intDetailCount].SellingQuantity = decimal.Parse(line.SellingQuantity.ToString());
                            // sods.OrderDtl[intDetailCount].OrderLine = int.Parse(line.OrderLine.ToString());
                            sods.OrderDtl[intDetailCount].XPartNum = line.CustXPartNum.ToString();
                            //sods.OrderDtl[intDetailCount].XPartNum = "(" + line.CustXPartNum.ToString() + ")";
                                OrderProxy.ChangeXPartNum(sods);
                            OrderProxy.ChangeCommissionable(sods);
                            //Need to call masterupdate not update 
                            OrderProxy.MasterUpdate(true, true, "OrderDtl", intCustomerNum, intSalesOrder, true, out blnOut, out strResponseMsg, out strCreditShipAction, out strDisplayMsg, out strCompliantMsg, out strResponseMsgOrdRel, sods);
                            //need to test
                            //if (line.Warehouse.ToString() == "Service")
                            //{ 
                                sods.OrderRel[intDetailCount].WarehouseCode = line.Warehouse.ToString();
                                OrderProxy.ChangeORelWarehouse(sods);
                                OrderProxy.MasterUpdate(true, true, "OrderRel", intCustomerNum, intSalesOrder, true, out blnOut, out strResponseMsg, out strCreditShipAction, out strDisplayMsg, out strCompliantMsg, out strResponseMsgOrdRel, sods);
                            //}
                        }

                        if (intSalesOrder != 0)
                        {
                            sods.OrderHed[0].UserChar1 = "PRINT";
                            sods.OrderHed[0].RowMod = "U";
                            //OrderProxy.Update(sods);
                            OrderProxy.MasterUpdate(true, true, "Orderhed", intCustomerNum, intSalesOrder, true, out blnOut, out strResponseMsg, out strCreditShipAction, out strDisplayMsg, out strCompliantMsg, out strResponseMsgOrdRel, sods);
                        }
                    }
                     // Email Ken that file was processed with Order and Line details
                     //SendMail();
                 }
                    
            }
            return true;
            }
            catch (Exception e)
            {

                Console.WriteLine("{0} Exception caught.", e);
                return false;

            }
        }  //private Boolean ProcessOrder(SalesOrderTransfer NewOrder)

Hi Ken,

Well done. Thanks for sharing your code. It seems like you are doing this through a dll Epicor.Mfg.BO.SalesOrder and Epicor.Mfg.IF.ISalesOrder? As I understand this is not an option when you want to post order through WSDL. I am doing something like this:

First of all here is what SalesOrderDataSetType class is accepting:

SalesOrderDataSetType ds = new SalesOrderDataSetType();
SalesOrderDataSetTypeOrderHed soDSHed = new SalesOrderDataSetTypeOrderHed();
SalesOrderDataSetTypeOrderDtl soDSDtl = new SalesOrderDataSetTypeOrderDtl();
SalesOrderDataSetTypeOrderRel soDSRel = new SalesOrderDataSetTypeOrderRel();

//Populate OrderHed fields
soDSHed.OrderNum = 0;
soDSHed.CustomerCustID = "1";
soDSHed.OrderDate = DateTime.Today;
soDSHed.RequestDate = DateTime.Today;
soDSHed.NeedByDate = DateTime.Today;
soDSHed.ShipToNum = "2";
soDSHed.ShipViaCode = "UPS";
soDSHed.OrderComment = "TestOrderComment";
soDSHed.FOB = "Test";
soDSHed.WebOrder = false;
soDSHed.TermsCode = "PP";
soDSHed.TaxRegionCode = "TEST";
soDSHed.TotalCharges = 20.30m;
soDSHed.TotalShipped = 2;
soDSHed.SalesRepCode1 = "TS";
soDSHed.BTCustNum = 1;
soDSHed.PONum = "Test";

ds.SalesOrderDataSet[0] = soDSHed;

//Populate OrderDtl fields
soDSDtl.POLine = "1";
soDSDtl.OrderLine = 1;
soDSDtl.PartNum = "408115";
soDSDtl.XPartNum = "";
soDSDtl.LineDesc = "TestDecr222";
soDSDtl.SellingQuantity = 2.0m;
soDSDtl.SalesUM = "EACH";
soDSDtl.UnitPrice = 15.0m;
soDSDtl.Reference = "Reftest";
soDSDtl.Commissionable = true;
soDSDtl.DiscountPercent = 10.00m;
soDSDtl.WarehouseCode = "MAIN";
soDSDtl.MktgCampaignID = "TES";
soDSDtl.SalesCatID = "TES";

ds.SalesOrderDataSet[1] = soDSDtl;

//Populate OrderRel fields. You have to include these fields, otherwise, you will get keys are missing in a parent table error.
soDSRel.Company = "TEST";
soDSRel.OrderNum = latestOrderNumber+1;
soDSRel.OrderLine = 1;

ds.SalesOrderDataSet[2] = soDSRel;

soService.SubmitNewOrder("TEST", ds, callContextIn, out callContextOut);

This is how I am populating objects and trying to send them. I noticed that Epicor is pretty responsive and gives a legit error if you are missing one of the required fields as I got with ShipViaCode. It’s not mentioned in the documentation lol, but it returned me an error that ShipViaCode is required.

I think you need to update the sales header to get the id. then use that sales id of the newly created order to create the line.

Sorry Ken, what kind of id are you talking about?

Alex have you done a trace in Epicor on how an order is created?
You should be calling the order creation process approximately in this fashion

GetNewOrderHed
ChangeCustomerInfo
Update
GetNewOrderDtl
SetLineDetail
Update

You can’t (at least not reliably) send the header and lines at the same time because of dependency issues, header needs to exist before the lines are added.
Do a trace in Epicor and then use my Trace tool (or Notepad) to look at the trance and see what data needs to be changed, send on every one of the calls.

1 Like

I meant OrderNum