Rest API V2 Batch Post - Help Needed

I was going through some rest documentation for Rest v2 and ran into an issue. The only way I see to post new records was to post one record at a time. Is there a way to batch post several entries at a time instead of a single one?

Functions! You can create a function to accept a string parameter of serialized JSON for your batch data and then parse it inside your function to cycler over the inputs and create the records.

3 Likes

I know absolutely nothing about functions. But, I will certainly be digging into those now. Thanks Aaron!

Functions are great, let me know if you need help with it!

Hi Aaron,
Would you mind sharing an example if you already have set up a function doing the similar? We’re trying to set up a function to create records for a UD table but completely stuck.

Hi Harry,
Are you stuck on the “inside” part of the function to create a UD record, or are you stuck on how to pass serialized data to a function, parse into multiple rows, and then consume?

1 Like

Hi Aaron,

I am actually working on this project with Harry.
The issue I am facing is the “inside” part of the function. I can serialize the data as JSON and send it through my .Net code to the function but, I just can not get the Epicor function to loop through the JSON data and add a new row to a UD table for each set of JSON data.
I was playing around with the Epicor Function and when I came to the “Invoke BO Method” block, I noticed that we could only use the custom methods that appear in the Swagger API and not the OData methods. Is there any way to get the OData methods to work in the Epicor Function interface?

Hello Malith,
I’ll show how I took a serialized input string from a function and parse it inside of a function.
I created the request signature with a string variable “productList”, which will represent the serialized string of products I will parse

Here is what my call to this function looks like with the serialized string:

Pass that string into your function via the body of your call.

Once inside the function, use a Custom Code block to parse your string and iterate over the results (note; I don’t know of a way to do this with pure widgets, so you must have a custom code block in this example).

I consume the variable by deserializing it like so:

//Add Order Lines

//iterate over incoming params to add part lines

dynamic prodList = Newtonsoft.Json.JsonConvert.DeserializeObject(productList);

//msg = prodList.ToString();
int rowCnt = 0;
foreach(var product in prodList)
{
        
        //Get New Order Dtl
        this.CallService<Erp.Contracts.SalesOrderSvcContract>(ss =>{
        ss.GetNewOrderDtl(ref tsSalesOrder, salesOrderNum); 
        
        //ChangePartNumMaster        
        string partNum = product.PartNum.ToString();
        bool lSubstitutePartExist = false;
        bool lIsPhantom = false;   
        
        string uomCode = "";
        Guid SysRowID = Guid.Empty;
        
        string rowType = "";       
        bool salesKitView = false;
        bool removeKitComponents = false;
        bool suppressUserPrompts = false;
        
        bool getPartXRefInfo = true;
        bool checkPartRevisionChange = true;
        bool checkChangeKitParent = true;
        
        string cDeleteComponentsMessage = "";
        string questionString = "";
        string cWarningMessage = "";
        
        bool multipleMatch  = false;
        bool promptToExplodeBOM = false;
        
        string cConfigPartMessage = "";
        string cSubPartMessage= "";
        string explodeBOMerrMessage = "";
        string cMsgType = "";
        
        bool multiSubsAvail = false;
        bool runOutQtyAvail = false;       
        
        tsSalesOrder.OrderDtl[rowCnt].PartNum = product.PartNum.ToString();
        tsSalesOrder.OrderDtl[rowCnt]["DrawNumFromQuote_c"] = product.LotNum.ToString();
        
        tsSalesOrder.OrderDtl[rowCnt].RowMod = "A";
        
        ss.ChangePartNumMaster(ref partNum, ref lSubstitutePartExist, ref lIsPhantom, ref uomCode, SysRowID, rowType, salesKitView, removeKitComponents, suppressUserPrompts, getPartXRefInfo, checkPartRevisionChange, checkChangeKitParent, out cDeleteComponentsMessage, out questionString, out cWarningMessage, out multipleMatch, out promptToExplodeBOM, out cConfigPartMessage, out cSubPartMessage, out explodeBOMerrMessage, out cMsgType, out multiSubsAvail, out runOutQtyAvail, ref tsSalesOrder);
        
        //ChangeSellingQtyMaster
        decimal ipSellingQuantity = 1.00M;
        bool chkSellQty = false;
        bool negInvTest = false;
        bool chgSellQty = true;
        bool chgDiscPer = true;
       //bool suppressUserPrompts = false;
        bool lKeepUnitPrice = true;
        string pcPartNum = product.PartNum.ToString();
        string pcWhseCode = "";
        string pcBinNum = "";
        string pcLotNum = "";
        int pcAttributeSetID = 0;
        string pcDimCode = "EA";
        decimal pdDimConvFactor = 1;
        string pcMessage = "";
        string pcNeqQtyAction = "";
        string opWarningMsg = "";
        string cSellingQuantityChangedMsgText = "";

        tsSalesOrder.OrderDtl[rowCnt].PartNum = product.PartNum.ToString();
        tsSalesOrder.OrderDtl[rowCnt]["DrawNumFromQuote_c"] = product.LotNum.ToString();
        
        tsSalesOrder.OrderDtl[rowCnt].RowMod = "A";
        
        ss.ChangeSellingQtyMaster(ref tsSalesOrder, ipSellingQuantity, chkSellQty, negInvTest, chgSellQty, chgDiscPer, suppressUserPrompts, lKeepUnitPrice,pcPartNum, pcWhseCode, pcBinNum, pcLotNum, pcAttributeSetID, pcDimCode,pdDimConvFactor, out pcMessage, out pcNeqQtyAction, out opWarningMsg, out cSellingQuantityChangedMsgText);
        
        //UpdateMaster
        bool lCheckForOrderChangedMsg = true;
        bool lcheckForResponse = true;
        string cTableName = "OrderDtl";
        int iCustNum = soldToCustNum;
        int iOrderNum = salesOrderNum;
        bool lweLicensed = false;
        bool lContinue = false;
        string cResponseMsg = "";
        string cCreditShipAction = "";
        string cDisplayMsg = "";
        string cCompliantMsg = "";
        string cResponseMsgOrdRel = "";
        string cAgingMessage = "";
        
        //Update Rep
        tsSalesOrder.OrderHed[0].SalesRepCode1 = "ARTHREX";        
        tsSalesOrder.OrderHed[0].SalesRepName1 = "Arthrex Commissions";
        tsSalesOrder.OrderHed[0].RowMod = "U";
        ss.MasterUpdate(lCheckForOrderChangedMsg,lcheckForResponse, cTableName, iCustNum, iOrderNum, lweLicensed, out lContinue, out cResponseMsg, out cCreditShipAction, out cDisplayMsg, out cCompliantMsg, out cResponseMsgOrdRel, out cAgingMessage, ref tsSalesOrder);
        
        //Invoke Function to ChangeTendonReserveStatus
        var result = this.EfxLib.UD100Functions.ChangeTendonReserveStatus(product.LotNum.ToString(), product.PartNum.ToString(), soldToCustNum, salesOrderNum,rowCnt+1, true);
        
        rowCnt++;
        }
      );
      
     
      //msg += product.PartNum.ToString()+ ": ";
}

Of note, to call a business object programatically (without widget), you can use the “CallService” method available to the function. You will still need to add any references to Business Objects in your library as well as to your scope.

this.CallService<Erp.Contracts.SalesOrderSvcContract>(ss =>{
        ss.GetNewOrderDtl(ref tsSalesOrder, salesOrderNum);  

To answer more questions; no, inside of Epicor you will use the Business Object methods, not OData methods. This is far easier, in my opinion, because you will have access to the datasets and methods available to the BO. Just take a client trace of any transaction and you will be able to re-create it using the InvokeBOMethod blocks, or programatically, if you choose.

8 Likes

Thanks a lot for your in-depth reply Aaron :slight_smile: