Call Function with a button click

I have created a function that calls a BAQ and does some processing. The BAQ has a date parameter. I have put a BPM on the Update function of UD39. I am not changing any data in UD39, just using the form as a means for the user to enter the date and click the button to run the function. I found some code for getting this to work but it appears that it expects a record to have been updated in the epiview, but I don’t have a record to update.

How do you invoke a BPM (or just the function itself) from the form custom code without an updated data record?

Kinetic UX or Classic?

1 Like

Classic

In the Epicor help there’s a whole section on calling functions in classic customizations. Take a look there

2 Likes

I think I need a bit of help with this. My function was running fine when I executed it using the Schedule Epicor Function, with my date value hard-coded. I want to pass it as a parameter with the REST code but I keep getting an error that the task was canceled, and I have no idea why. The message detail isn’t very specific.

I set up a request and response parameter (although I am not using the response for anything)


I then have my code in my form set up as follows to call this function:

	private void epiButtonC1_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		EpiDateTimeEditor ApDate = (EpiDateTimeEditor)csm.GetNativeControlReference("50ee69c5-6d0c-463c-bc8a-5ca4660bb791");
		MessageBox.Show("In Call");
		string ApiKey = "RestKey";
		var restClient = new RestClientBuilder()
    		.SetDefaultApiKey(ApiKey)
    		.UseSession(this.oTrans.CoreSession)
    		.Build();
		var functionContent = new RestContent(new { ApplyDate = ApDate.ToString() })
    			.SetApiKey(ApiKey);


		MessageBox.Show("Before Call");
		try{
		var functionResponse =  restClient.Function.Post("MyLibrary", "SetFunction", functionContent, published: true);
		MessageBox.Show("After Call");
		}
		catch (ArgumentException e)
		{
			MessageBox.Show("Error: {e.Messsage}");
		}

	}

I put the try and catch here to see if I could catch anything but I doesn’t trigger an exception.

I have been trying to run this with the Schedule Epicor Function process. It properly prompts me for my Apply Date parameter, but the error I am getting is “Parameter should be specified”. I am confused as to where it should be specified since I have it set up in the function signature.

what if you change it to more general catch(Exception e)

1 Like

I have removed the try and catch blocks but I still get the error:

Exception executing library ‘MyLibrary’ function ‘’:
System.ArgumentException: Parameter should be specified
Parameter name: id
at Epicor.Functions.FunctionId…ctor(String id) in C:_Releases\ICE\RL10.2.600.0FW\Source\Server\Internal\Lib\Epicor.Functions.Core\FunctionId.cs:line 21
at Ice.Internal.Task.ScheduledFunction.ExecuteFunction.GetFunctionAdapter(String libraryId, String functionId) in C:_Releases\ICE\RL10.2.600.0FW\Source\Server\Internal\Task\ScheduledFunction\ExecuteFunction.cs:line 119
at Ice.Internal.Task.ScheduledFunction.ExecuteFunction.RunProcess(Int64 instanceTaskNum, String outputFileName) in C:_Releases\ICE\RL10.2.600.0FW\Source\Server\Internal\Task\ScheduledFunction\ExecuteFunction.cs:line 48

this is server code, you asked question about call on button click, which is client.
What exactly you are trying to achieve at the end? What happens when you click on that button now?

1 Like

I was trying to get the function running again via the Schedule Epicor Function. I didn’t think about it but I had developer mode on and it was putting me into the execution/parameters (or whatever it is called) for a different function. Once I turned developer mode off it ran (duh).

I have proved that the function will run with the parameter via the Schedule Epicor Function (meaning no errors with the function itself). That being said, my code within the customized form still is throwing the error. I only have one parameter so I was hoping to just do the Anonymous type parameters as described in the help by I am still missing something. Perhaps it has something to do with the way I am trying to get the date entry by the user into the call parameter. I get the value from the control into ApDate then I have this statement:

		var functionContent = new RestContent(new { ApplyDate = ApDate.ToString() })
    			.SetApiKey(ApiKey);

Could there be something wrong with doing that?

Post a screenshot of what you schedule when you schedule it

If I had to guess that Date.ToString() isn’t generating the right format… Try

ApDate.ToString("o");

But that’s a shot in the dark.

Either that or don’t cast it as a string at all.

It does not like the “o” in the ToString(). I have tried other ways of not converting it at all and trying to pass a different string variable in but it doesn’t like it. One of the errors I get is

1 Like

You can try changing your function signature to accept a string, then do the conversion inside the function. Dates and JSON seem to have problems.

also system.DateTime? is not the same as system.DateTime. With the nullable, I believe you’re going to have to pull .Value out of the input.

If it’s not going to be null, I would suggest not using the nullable type.

I have already changed it to be a string:


It doesn’t like passing a date type via the query execution parameter so I changed it to a string. This is what I have in my function, which works when I call it via the Schedule Epicor Function.

 dsBAQ.ExecutionParameter[0].ParameterValue = ApplyDate; 

It has to be something in the way I am callling it from my custom form code.

^ please

Here is the snapshot of the function schedule
image

I’m not sure why you feel the need to white out the Libaray and Method names but ok… I’m assuming then you changed the names in this post too?

private void epiButtonC1_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
		EpiDateTimeEditor ApDate = (EpiDateTimeEditor)csm.GetNativeControlReference("50ee69c5-6d0c-463c-bc8a-5ca4660bb791");
		MessageBox.Show("In Call");
		string ApiKey = "RestKey";
		var restClient = new RestClientBuilder()
    		.SetDefaultApiKey(ApiKey)
    		.UseSession(this.oTrans.CoreSession)
    		.Build();
		var functionContent = new RestContent(new { ApplyDate = ApDate.ToString() })
    			.SetApiKey(ApiKey);


		MessageBox.Show("Before Call");
		try{
		var functionResponse =  restClient.Function.Post("MyLibrary", "SetFunction", functionContent, published: true);
		MessageBox.Show("After Call");
		}
		catch (ArgumentException e)
		{
			MessageBox.Show("Error: {e.Messsage}");
		}

	}

What does the function itself look like?

1 Like

Alter the line @josecgomez provided as shown below and try that again

((DateTime)ApDate).ToString("O");

Here is the function code. Right now it calls the query then just writes information to a file. As I said, this works fine when I schedule it but my call to it from my code does not.

  DataTable dt, ResDt;
  DataSet ds, ResDs;
  string Emp, ResGroup;
  decimal NewOHRate = 0;
  decimal NewBurdenRate = 0;
  int lbrHead = 0, lbrDetail = 0, tranNum = 0;
  
  string path = @"\\myserver.com\EpicorData\LaborRateUpdates_1.txt";
  StreamWriter sw = new StreamWriter(path);
  var context = Ice.Services.ContextFactory.CreateContext<ErpContext>(); 
  
using (var svc = Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.DynamicQuerySvcContract>(context))
{ 
sw.WriteLine("in using " + ApplyDate.ToString());
   Ice.Tablesets.DynamicQueryTableset dsQuery = svc.GetByID("OH_Updates");   
  if (dsQuery != null) 
    { 
        Ice.Tablesets.QueryExecutionTableset dsBAQ = svc.GetQueryExecutionParameters(dsQuery);
          dsBAQ.ExecutionParameter[0].ParameterID = "ApplyDate"; 
          dsBAQ.ExecutionParameter[0].IsEmpty = false;  
          dsBAQ.ExecutionParameter[0].ParameterValue = ApplyDate;     
          //dsBAQ.ExecutionParameter[0].ParameterValue = "06/25/2023";
          ds = svc.Execute(dsQuery,dsBAQ); 
          dt = ds.Tables[0];   
          int rcount = dt.Rows.Count; 
          //sw.WriteLine("record count " + rcount.ToString());       
          foreach (DataRow dr in dt.Rows)
          {       
            Emp = dr["LaborDtl_EmployeeNum"].ToString();
            ResGroup = dr["LaborDtl_ResourceGrpID"].ToString();
            lbrHead = (int)dr["LaborDtl_LaborHedSeq"];
            lbrDetail = (int)dr["LaborDtl_LaborDtlSeq"];

            //if this is a project labor record write an error out, else process it
            if (dr["LaborDtl_LaborType"].ToString() == "I")
            {
              var LabDtl = Db.LaborDtl.Where(e=>e.LaborHedSeq == lbrHead && e.LaborDtlSeq == lbrDetail);
              foreach (var LLine in LabDtl)      
              {
                //there should only be one line
                sw.WriteLine("New burden rate - " + dr["EmpBasic_Name"].ToString() + " Rate= " + LLine.BurdenRate.ToString() + ", Payroll Date=" + dr["LaborDtl_PayrollDate"].ToString() + ", Indirect Code=" + dr["LaborDtl_IndirectCode"].ToString());
               }
            }
            else
            {
                sw.WriteLine("Non-Indirect Labor Record found: " + dr["EmpBasic_Name"].ToString() + ", Payroll Date=" + dr["LaborDtl_PayrollDate"].ToString() + ", Project=" + dr["LaborDtl_ProjectID"].ToString());
            }    
                
          }        
     }
     
}

sw.Close();

I would start with swapping out the service call with the one for EFX. Don’t hack your way into the context and use the BPM methods.

this.CallService<DynamicQuerySvcContract>(svc=> {
// code here
});

use a using for StreamWriter as well.

Don’t hack your way into the context.

Part of the difference here to bear in mind, that could cause you trouble, is that the server runs the scheduled function with it’s auth and the client runs the function with the clients auth.

1 Like