This platform does not support distributed transactions error when Function called from BPM

I’m working on migrating a process from Service Connect workflows to functions. The service connect workflow picks up a file created by a pre-processing BPM and uses the data to create a pack. My first approach was to have the pre-processing BPM call the Kinetic function(s) that mimic the SC workflow. Unfortunately, when my function calls the Customer shipment UpdateExt method, get stopped by the distributed transactions error.

Based on the various topics that have been written about this error, it seems that the pre-processing BPM is running in a transaction and the UpdateExt also attempts to run in a transaction. FYI setting the transaction switch on the function makes the problem appear earlier in the execution of the function.

It seems that to avoid this problem, I need to redesign the process so the function can trigger off of a data directive. Before I head down that path, does anyone have another suggestion for a solution?

Try checking (or unchecking). Require transactions in the function

That works for me 99% of the time with this error

Describe your whole workflow in detail if you don’t mind.

@klincecum These may be more details than you were looking for.

We have a customization that is used to create packing slips for orders that have many releases all shipping the same product to different locations. This visual might help -

The “batch” information is stored in ice.UD100. The Release information is pulled into ice.UD100A.

When the Create Packs button is pushed, it will cause the UD100 pre-processing BPM to fire. The BPM was responsible for writing the pack and shipment line information for an XML file for each pack. Service Connect would then pick up each XML file and create a Pack from the data in the XML file.

It was my intention to use a function to create the ShipHead record when the ShipHead data was written to the XML file and then call a ShipDtl function every time the shipment line information was written to XML.

Here is my function flow for creating the ShipHead record -

If I strip everything out of the BPM and just use it to call the function with hard coded parameters, the function works as intended.

The complication comes when it is called in from the BPM that is looping through a couple datasets to get the values to use for function parameters

int RunTime = 0;
int wkShipQty;
DateTime wkShipDate = DateTime.Now;

bool sFound = false;
bool lockQty = false;
bool lastPack = false;
int sKntr = 0;
int sSize = -1;
var sShipTo  = new string [3000];

int tKntr = 0;
int tSize = -1;
var tOrdNum  = new string[3000];
var tOrdLine = new string[3000];
var tOrdRel  = new string[3000];
var tPartNum = new string[3000];
var tShipQty = new string[3000];
var tShipTo  = new string[3000];
var tLockQty = new string[3000];

string PackOrder = string.Empty;
string PackShipTo = string.Empty;
decimal OrdNum = 0;
decimal OrdLin = 0;
decimal OrdRel = 0;
int fileKntr = 0;
int PackNum = 0;

Ice.Tables.UD40   UD40;
Ice.Tables.UD100A UD100A;

RunTime = Convert.ToInt32(DateTime.Now.TimeOfDay.TotalSeconds);
//InfoMessage.Publish("Message one", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual);
if (debug == true)
{
  Ice.Diagnostics.Log.WriteEntry("CreatePacks2023 - debug 1 beginning UD100 Loop");
}
foreach (var ttUD100_xRow in ds.UD100)
{
  var ttUD100Row = ttUD100_xRow;
  if (debug == true)
{
  Ice.Diagnostics.Log.WriteEntry("CreatePacks2023 - debug 2 beginning UD100A Loop");
}
  foreach (var UD100A_iterator in (from UD100A_Row in Db.UD100A
                        where string.Compare(UD100A_Row.Company, ttUD100Row.Company, true) == 0
                           && string.Compare(UD100A_Row.Key1, ttUD100Row.Key1, true) == 0
                           && string.Compare(UD100A_Row.Key2, ttUD100Row.Key2, true) == 0
                           && string.Compare(UD100A_Row.Key3, ttUD100Row.Key3, true) == 0
                           && string.Compare(UD100A_Row.Key4, ttUD100Row.Key4, true) == 0
                           && string.Compare(UD100A_Row.Key5, ttUD100Row.Key5, true) == 0
													 && UD100A_Row.CheckBox01 == true
           select UD100A_Row))
  {
	    UD100A = UD100A_iterator;
			PackOrder = UD100A.ChildKey1;
			UD100A.CheckBox01 = false;
			UD100A.CheckBox02 = true;
			UD100A.Date01 = DateTime.Now;
			UD100A.Number07 = RunTime;
			UD100A.ShortChar01 = Session.UserID;
			wkShipQty = Convert.ToInt32(UD100A.Number04);
			wkShipDate = Convert.ToDateTime(ttUD100Row.Date02);
       // InfoMessage.Publish("Message two", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual);
			tSize++;
			OrdNum = UD100A.Number01;
			OrdLin = UD100A.Number02;
			OrdRel = UD100A.Number03;
			tOrdNum  [tSize] = OrdNum.ToString("0");
			tOrdLine [tSize] = OrdLin.ToString("0");
			tOrdRel  [tSize] = OrdRel.ToString("0");
			tPartNum [tSize] = UD100A.ShortChar02;
			tShipQty [tSize] = Convert.ToString(wkShipQty);
			tShipTo  [tSize] = UD100A.ShortChar04;
			//tShipTo  [tSize] = UD100A.Character04;
			if (UD100A.CheckBox03 == true) 		tLockQty [tSize] = "true";
			else															 tLockQty [tSize] = "false";
			sFound = false;
			string wkShipTo = UD100A.ShortChar04;
			//string wkShipTo = UD100A.Character04;
			for (sKntr = 0; sKntr <= sSize; sKntr++) 
			{
					if ( sShipTo [sKntr] == wkShipTo ) 
					{
							sFound = true;
					}
			}
			if (sFound == false) 
			{
					sSize++;
					sShipTo [sSize] = wkShipTo;
			}
  }
  
  if (debug == true)
{
  Ice.Diagnostics.Log.WriteEntry("CreatePacks2023 - debug 3 beginning UD40 Loop");
}
  foreach (var UD40_iterator in (from UD40_Row in Db.UD40
                        where string.Compare(UD40_Row.Company, ttUD100Row.Company, true) == 0
                           && string.Compare(UD40_Row.Key1, "ESCChannels", true) == 0
           select UD40_Row))
  {
	    UD40 = UD40_iterator;
	    
			for (sKntr = 0; sKntr <= sSize; sKntr++) 
			{   //InfoMessage.Publish("Message three", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual);
					fileKntr = 1000 + sKntr;
					string OutFile = UD40.Character02 + @"BatchPackSlips\Pack" + fileKntr.ToString() + "_" + RunTime + ".xml";
					StreamWriter tw = new StreamWriter(OutFile);
					tw.WriteLine("<PackSlip>");
					tw.WriteLine("  <Company>" + ttUD100Row.Company + "</Company>" );
					tw.WriteLine("  <ShipDate>" + wkShipDate.ToString("yyyy-MM-dd") + "</ShipDate>" );
					tw.WriteLine("  <ShipVia>" + ttUD100Row.ShortChar01 + "</ShipVia>" );
					tw.WriteLine("  <BatchNum>" + ttUD100Row.Key1 + "</BatchNum>" );
					if (ttUD100Row.CheckBox03 == true) 		tw.WriteLine("  <MarkShipped>true</MarkShipped>" );
					else															 		tw.WriteLine("  <MarkShipped>false</MarkShipped>" );
					if (ttUD100Row.CheckBox04 == true) 		tw.WriteLine("  <PrintPacks>true</PrintPacks>" );
					else															 		tw.WriteLine("  <PrintPacks>false</PrintPacks>" );
					if (debug == true)
{
  Ice.Diagnostics.Log.WriteEntry("CreatePacks2023 - debug 4 Setting lastPack");
}
					if (sKntr == sSize)							 		 {tw.WriteLine("  <LastPack>true</LastPack>" );
					lastPack = true;}
					else															 		tw.WriteLine("  <LastPack>false</LastPack>" );
					tw.WriteLine("  <PackOrder>" + PackOrder + "</PackOrder>");
					tw.WriteLine("  <PackShipTo>" + sShipTo [sKntr] + "</PackShipTo>");
//Call ShipHead function
if (debug == true)
{
  Ice.Diagnostics.Log.WriteEntry("CreatePacks2023 - debug 5 Calling Create ShipHead");
}
          var response = this.InvokeFunction("BatchPack","CreateShipHead", ttUD100Row.Key1,wkShipDate, ttUD100Row.ShortChar01, debug);
          PackNum = (int)response[0];

					for (tKntr = 0; tKntr <= tSize; tKntr++) 
					{
							if ( string.Compare(tShipTo [tKntr], sShipTo [sKntr], true) == 0)
							{
									tw.WriteLine("  <ShipLine>");
									tw.WriteLine("    <OrderNum>"  + tOrdNum  [tKntr] + "</OrderNum>" );
									tw.WriteLine("    <OrderLine>" + tOrdLine [tKntr] + "</OrderLine>" );
									tw.WriteLine("    <OrderRel>"  + tOrdRel  [tKntr] + "</OrderRel>" );
									tw.WriteLine("    <PartNum>"   + tPartNum [tKntr] + "</PartNum>" );
									tw.WriteLine("    <ShipQty>"   + tShipQty [tKntr] + "</ShipQty>" );
									tw.WriteLine("    <LockQty>"   + tLockQty [tKntr] + "</LockQty>" );
									tw.WriteLine("  </ShipLine>");
//Call ShipDtl function
//         this.InvokeFunction("BatchPack","CreateShipDtl", PackNum ,int.Parse(tOrdNum [tKntr]), int.Parse(tOrdLine [tKntr]), int.Parse(tOrdRel [tKntr]), tPartNum [tKntr], int.Parse(tShipQty [tKntr]), Convert.ToBoolean(tLockQty [tKntr]) , debug ); 
							}
      }
					tw.WriteLine("</PackSlip>");
					tw.Close();
//Call Mark Ship and Print
//        this.InvokeFunction("BatchPack","ShipPrint",PackNum, ttUD100Row.CheckBox03, ttUD100Row.CheckBox04, lastPack, debug); 
			}
	}

  RunTime = Convert.ToInt32(DateTime.Now.TimeOfDay.TotalSeconds) - RunTime;
  ttUD100Row.CheckBox01 = false;
  ttUD100Row.Date01 = DateTime.Now;
  ttUD100Row.Number03 = Convert.ToInt32(DateTime.Now.TimeOfDay.TotalSeconds);
  ttUD100Row.Number04 = RunTime;
  ttUD100Row.ShortChar02 = Session.UserID;
  
}

@josecgomez When the “Requires Transaction” checkbox is not set, the function will error out when the UpdateExt method is called.

When the “Requires Transaction” checkbox is set, then the function will error out sooner when the GetNewShipHead method is called.

Nope, those are the droids I’m looking for.

Obi-Wan Kenobi Not The Droids GIF

When I have some more brain power, I’ll read it again.

There is probably some simpler architecture to do this than you have,
no need to recreate it exactly as before.

Thanks for the suggestions. I’m taking a different approach now, rather than following the original Service Connect flow.