Thursday, November 15, 2018

.NET code to create a Dynamics GP batch with eConnect

By Steve Endow

I just posted a story about how I discovered that sometimes I need to explicitly create Dynamics GP batches using eConnect in order to control the batch posting date.

Here's the code that I used.

First, I check to see if the batch exists.


 public static bool BatchExists(string database, int series, string batchSource, string batchID)   
 {  
   try  
   {  
     string commandText = "SELECT COUNT(*) AS RecordCount FROM dbo.SY00500 WHERE SERIES = @SERIES AND BCHSOURC = @BCHSOURC AND BACHNUMB = @BACHNUMB"; // AND GLPOSTDT = @GLPOSTDT";  
     SqlParameter[] sqlParameters = new SqlParameter[3];  
     sqlParameters[0] = new SqlParameter("@SERIES", System.Data.SqlDbType.Int);  
     sqlParameters[0].Value = series;  
     sqlParameters[1] = new SqlParameter("@BCHSOURC", System.Data.SqlDbType.VarChar, 15);  
     sqlParameters[1].Value = batchSource.Trim();  
     sqlParameters[2] = new SqlParameter("@BACHNUMB", System.Data.SqlDbType.VarChar, 15);  
     sqlParameters[2].Value = batchID.Trim();  
     string result = DataAccess.ExecuteScalar(database, CommandType.Text, commandText, sqlParameters);  
     int recordCount = Convert.ToInt32(result);  
     if (recordCount > 0)  
     {  
       return true;  
     }  
     else  
     {  
       return false;  
     }  
   }  
   catch (Exception ex)  
   {  
     Log.Write("An unexpected error occurred in DataAccess.BatchExists: " + ex.Message, true);  
     return false;  
   }  
 }  


If the batch does exist, I make sure to update the posting date.




 public static bool SetBatchPostingDate(string database, int series, string batchSource, string batchID, string glPostDate)  
 {  
   try  
   {  
     string commandText = "UPDATE dbo.SY00500 SET GLPOSTDT = @GLPOSTDT WHERE SERIES = @SERIES AND BCHSOURC = @BCHSOURC AND BACHNUMB = @BACHNUMB AND GLPOSTDT <> @GLPOSTDT";  
     SqlParameter[] sqlParameters = new SqlParameter[4];  
     sqlParameters[0] = new SqlParameter("@SERIES", System.Data.SqlDbType.Int);  
     sqlParameters[0].Value = series;  
     sqlParameters[1] = new SqlParameter("@BCHSOURC", System.Data.SqlDbType.VarChar, 15);  
     sqlParameters[1].Value = batchSource.Trim();  
     sqlParameters[2] = new SqlParameter("@BACHNUMB", System.Data.SqlDbType.VarChar, 15);  
     sqlParameters[2].Value = batchID.Trim();  
     sqlParameters[3] = new SqlParameter("@GLPOSTDT", System.Data.SqlDbType.DateTime);  
     sqlParameters[3].Value = Convert.ToDateTime(glPostDate);  
     int recordCount = DataAccess.ExecuteNonQuery(database, CommandType.Text, commandText, sqlParameters);  
     if (recordCount > 0)  
     {  
       return true;  
     }  
     else  
     {  
       return false;  
     }  
   }  
   catch (Exception ex)  
   {  
     Log.Write("An unexpected error occurred in DataAccess.SetBatchPostingDate: " + ex.Message, true);  
     return false;  
   }  
 }  


And if the batch does not exist, I create it using taCreateUpdateBatchHeaderRcd.


 public bool CreateBatch(string batchID, string comment, int series, string postingDate, string batchSource, decimal batchTotal, int origin, int numOfTrx, int postToGL)  
 {  
   //Series:  
   //1 = All;  
   //2 = Financial;  
   //3 = Sales;  
   //4 = Purchasing;  
   //5 = Inventory;  
   //6 = Payroll;  
   //7 = Project  
   //Some batch source values are:  
   //PM_Trxent - Series 4   
   //Rcvg Trx Entry - Series 4  
   //Rcvg Trx Ivc - Series 4  
   //ASMENT  
   //GL_Clearing  
   //GL_Normal  
   //IV_Trans  
   //IV_Trxent  
   //PA_ML - Series 7  
   //PA_TS  
   //RM_Cash  
   //RM_Sales  
   //Sales Entry  
   //Origin:  
   //For GL:  
   //1= General entry  
   //For IV:   
   //1=Transaction entry;  
   //2=Transfer entry   
   //For PM:   
   //1=Transaction entry;   
   //2=Computer check;  
   //3=Manual payment   
   //For RM:   
   //1=Transaction entry;   
   //2=Cash receipts entry  
   //For SOP:  
   //1=Sales transaction entry  
   //Post to GL:  
   //Defaults to 0 if nothing is passed in.  
   //0 = False  
   //1 = True  
   taCreateUpdateBatchHeaderRcd batchHeader = new taCreateUpdateBatchHeaderRcd();  
   batchHeader.BACHNUMB = batchID.Trim();  
   batchHeader.BCHCOMNT = comment.Trim();  
   batchHeader.SERIES = series;  
   batchHeader.GLPOSTDT = postingDate;  
   batchHeader.BCHSOURC = batchSource;  
   batchHeader.ORIGIN = origin;  
   batchHeader.NUMOFTRX = numOfTrx;  
   SMTransactionBatchType batchType = new SMTransactionBatchType();  
   batchType.taCreateUpdateBatchHeaderRcd = batchHeader;  
   SMTransactionBatchType[] batchTypeArray = { batchType };  
   eConnectType eConnect = new eConnectType();  
   eConnect.SMTransactionBatchType = batchTypeArray;  
   MemoryStream memStream = new MemoryStream();  
   XmlSerializer serializer = new XmlSerializer(eConnect.GetType());  
   serializer.Serialize(memStream, eConnect);  
   memStream.Position = 0;  
   XmlDocument xmlDocument = new XmlDocument();  
   xmlDocument.Load(memStream);  
   memStream.Close();  
   eConn eConn = new eConn();  
   string response = string.Empty;  
   bool returnValue = eConn.InsertTransaction(ref response, xmlDocument.OuterXml, Controller.Instance.Model.GPDatabase);  
   if (returnValue == false)  
   {  
     Log.Write("\tFailed to create batch " + batchID, true);  
     Log.Write(response, true);  
     return false;  
   }  
   else  
   {  
     return true;  
   }  
 }  


And this is the InsertTransaction call in my "eConn" class, which calls eConnect:


public bool InsertTransaction(ref string response, string transactionXML)  //, string companyDB
{
    GP gp = new GP(gpUserID, gpPassword, gpDatabase, log);
    string connString = gp.ConnectionStringWindows();

    string returnValue = string.Empty;  //GP 2010
    //bool success = false;  //GP 10
    
    response = "";

    eConnectMethods eConnCall = new eConnectMethods();

    try
    {
        returnValue = eConnCall.CreateTransactionEntity(connString, transactionXML);
        return true;
    }
    catch (eConnectException ex)
    {
        response = "eConnectException: " + ex.Message;
        response += "\r\nInner Exception: " + ex.InnerException;
        response += "\r\n" + transactionXML;
        return false;
    }
    catch (SqlException ex)
    {
        response = "SqlException: " + ex.Message;
        response += "\r\nInner Exception: " + ex.InnerException;
        response += "\r\n" + transactionXML;
        return false;
    }
    catch (Exception ex)
    {
        response = "Exception: " + ex.Message;
        response += "\r\nInner Exception: " + ex.InnerException;
        response += "\r\n" + transactionXML;
        return false;
    }
}



The eConnect taCreateUpdateBatchHeaderRcd node is a bit odd.  Based on my limited testing, it will only update an existing batch in some cases.  For example, if a Payables Transaction Entry batch already exists, if you submit the batch request to eConnect with a different posting date, eConnect will not update the batch record.  I haven't bothered to dig through the stored procedure to decipher when it will actually perform an update.

So that is why I have my own routine to update the batch posting date if the batch already exists.



You can also find him on Twitter and YouTube







4 comments:

  1. I realize this posting is a year old, but it's code that would be very helpful to me if I could get it to work. There is a line in the last part that apparently needs a reference I don't have.

    eConn eConn = new eConn();

    I've used the serialize code to create customers and SOP transactions, but this is the first time I've tried creating a batch record only. I could do so via a SQL call but I prefer to use eConnect via .NET

    Thanks for your help.

    ReplyDelete
  2. I have updated the sample code to show the InsertTransaction method. It's just a call to the eConnect CreateTransactionEntity method.

    ReplyDelete
  3. Hi Steve. I enjoy your blog and I hope you are still actively updating it. Is there also a way in eConnect to POST a batch? I am working with a dev environment and I can create RM Invoices in a batch, but I can not see any way to post the batch without having a GP UI/Front End installed. Thanks in advance!
    Elie

    ReplyDelete
    Replies
    1. You cannot post a batch using eConnect--it is only for data import & export.

      Dynamics GP does not have any native features to automate batch posting.

      Automating batch posting requires a custom solution using either: 1) A macro , 2) Dexterity or 3) VS Tools

      I resell a solution called Post Master Enterprise for Dynamics GP that fully automates batch posting and runs as a Windows Service:

      https://envisagesoftware.com/post-master

      If you're interested in learning more, you can contact us at:

      https://envisagesoftware.com/contact-us

      Delete

All comments must be reviewed and approved before being published. Your comment will not appear immediately.

How many digits can a Business Central Amount field actually support?

 by Steve Endow (If anyone has a technical explanation for the discrepancy between the Docs and the BC behavior, let me know!) On Sunday nig...