Script Editor - MES Customization Calculation

In the “Start Production Activity” for MES, I would like to add additional fields for informational requirement from my users. I did a foreign key link to JobOper to get the ProductionQty and QtyCompleted. I had both of these displayed in the textbox. My user asked if I can take these 2 texbox and calculate the difference to tell them the remaining balance for each operation.

I thought since the 2 textboxes are strings, i convert them into double and then do the math. However, when I did that I get the following error.

Any ideas how I can take these 2 textboxes and do calculation?

Never mind. I caught where I placed the variable. I moved it down to InitializeCustomcode and it’s fine now.

I do have a question though. Does anyone know which Custom Object Explorer I would need to have it do it’s calculation after it pull in the Production Qty and Completed Qty to get my remaining balance?

I just want to take 2 texboxes that pulled information from database and calculate to get my remaining balance which i put into another textbox.

1 Like

Heres one way, you pass the job,asm, and opr

Add a reference to BOReader

	void GetQtyInfo(string jobNum, int asmNum, int opNum)
	{
		try
		{
			
	             Ice.Proxy.Lib.BOReaderImpl _bor = WCFServiceSupport.CreateImpl<Ice.Proxy.Lib.BOReaderImpl>((Ice.Core.Session)oTrans.Session, Epicor.ServiceModel.Channels.ImplBase<Ice.Contracts.BOReaderSvcContract>.UriPath);
	                DataSet ds2 = _bor.GetList("Erp:BO:JobOperSearch", "JobNum = '"+jobNum+"' AND AssemblySeq = "+asmNum.ToString()+" AND OprSeq = "+opNum.ToString(), "");//,ProdQty,ReceivedQty,WIPQty");  //namespace,whereclause,columns
		               
				 if(ds2 != null)
				{
				//				epiUltraGridC1.DataSource = ds2.Tables[0];
					numCompletedQty.Value = ds2.Tables[0].Rows[0]["QtyCompleted"];
					numExpectedQty.Value = ds2.Tables[0].Rows[0]["RunQty"];
				}
		}
		catch(Exception e)
		{
			MessageBox.Show("Error: "+e.Message);
		}
	}
1 Like

Chris, what calls GetQtyInfo?

Chris,

To add the BOReader reference, from customization tools dialog–>Tools–>Custom Assembly Reference Manager, which dll is it? I am on Epicor 10.2, and I don’t see anything related to BOReader or Ice.Prox.Lib.BOReaderImpl.dll?

@cchang a recommendation not related to your question, for your own sake you should always rename the controls from their default value (epiLabelC1, epiLabelC3) to something that makes sense in the context of it’s use. IE rename epiLabelC1 to lblProdQty and epiLabelC3 to lblQtyComplete

Otherwise you’ll come back in 6 months and have no idea what in the world is epiLabelC1

Sorry for the unsolicited 2 cents, but my programmer brain twitches when it sees default control names

Carry on! :slight_smile:

3 Likes

I guess it makes the most sense to detect the change on the last item, in this case operation. (Bcuz when job or asm changes, operation is reset)

In general (pseudo) terms, it would be kinda like

GetRef to the EpiDataView that has your job/asm/op
EpiDataView myEDV = oTrans.Factory(“Name of the correct EDV”);
Put a field change event on operation field

OnFieldChange(Operation) =>
{
get the job/asm/op from the epidataview (myEDV)
Call GetQtyInfo
}

Also the DLL is:
Ice.Contracts.Lib.BOReader.dll

1 Like

Thanks for the help Chris. So I went and tried to detect change, however, it seems to be pulling operation 10 whenever I open my program. Also I can’t seem to get calling “GetQtyInfo”. Is there something I am missing my my code?

Here’s the entire code:

// **************************************************
// Custom code for StartProdForm
// Created: 4/2/2018 11:16:08 AM
// **************************************************

extern alias Erp_Contracts_BO_JobEntry;
extern alias Erp_Contracts_BO_JobAsmSearch;
extern alias Erp_Contracts_BO_JobOperSearch;
extern alias Erp_Contracts_BO_Resource;
extern alias Erp_Contracts_BO_ResourceGroup;
extern alias Erp_Contracts_BO_EmpBasic;

using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Erp.Adapters;
using Erp.UI;
using Ice.Lib;
using Ice.Adapters;
using Ice.Lib.Customization;
using Ice.Lib.ExtendedProps;
using Ice.Lib.Framework;
using Ice.Lib.Searches;
using Ice.UI.FormFunctions;

public class Script
{
// ** Wizard Insert Location - Do Not Remove ‘Begin/End Wizard Added Module Level Variables’ Comments! **
// Begin Wizard Added Module Level Variables **
private static string mJobOper;
private DataView Start_DataView;
// End Wizard Added Module Level Variables **

// Add Custom Module Level Variables Here **

public void InitializeCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
	// Begin Wizard Added Variable Initialization

	this.Start_DataView = this.Start_Row.dataView;
	this.Start_DataView.ListChanged += new ListChangedEventHandler(this.Start_DataView_ListChanged);
	mJobOper=string.Empty;

	this.baseToolbarsManager.ToolClick += new Infragistics.Win.UltraWinToolbars.ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	// End Wizard Added Variable Initialization

	// Begin Wizard Added Custom Method Calls
		
	// End Wizard Added Custom Method Calls
}

public void GetQtyInfo(string jobNum, int asmNum, int opNum)
{

	try
	{
             Ice.Proxy.Lib.BOReaderImpl _bor = WCFServiceSupport.CreateImpl<Ice.Proxy.Lib.BOReaderImpl>((Ice.Core.Session)oTrans.Session, Epicor.ServiceModel.Channels.ImplBase<Ice.Contracts.BOReaderSvcContract>.UriPath);
                DataSet ds2 = _bor.GetList("Erp:BO:JobOperSearch", "JobNum = '"+jobNum+"' AND AssemblySeq = "+asmNum.ToString()+" AND OprSeq = "+opNum.ToString(), "");
	               
			 if(ds2 != null)
			{
				txtQtyCompleted.Value = ds2.Tables[0].Rows[0]["QtyCompleted"];
				txtProdQty.Value = ds2.Tables[0].Rows[0]["RunQty"];
			
			}
	MessageBox.Show(Convert.ToString(txtQtyCompleted));
	}
	catch(Exception e)
	{
		MessageBox.Show("Error: "+e.Message);
	}
}

public void DestroyCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
	// Begin Wizard Added Object Disposal


	this.Start_DataView.ListChanged -= new ListChangedEventHandler(this.Start_DataView_ListChanged);
	this.Start_DataView = null;
	mJobOper=string.Empty;

	this.baseToolbarsManager.ToolClick -= new Infragistics.Win.UltraWinToolbars.ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	// End Wizard Added Object Disposal

	// Begin Custom Code Disposal

	// End Custom Code Disposal
}


public void JobOper()
{
//Get reference to job operation
EpiDataView edvJobOper=(EpiDataView)oTrans.EpiDataViews["Start"];
	if(((edvJobOper.dataView[0]["OprSeq"].ToString() !=null)&&(Convert.ToInt32(edvJobOper.dataView[0]["OprSeq"].ToString()) > 0)))
	{
		string strJobOper = edvJobOper.dataView[0]["OprSeq"].ToString();
		if ((mJobOper != strJobOper))
		{
			mJobOper = strJobOper;
			//GetQtyInfo();
			
			MessageBox.Show(mJobOper);
		}
		else
		{
			mJobOper=string.Empty;
		}
	}

}

private void Start_DataView_ListChanged(object sender, ListChangedEventArgs args)
{
	// ** Argument Properties and Uses **
	// Start_DataView[0]["FieldName"]
	// args.ListChangedType, args.NewIndex, args.OldIndex
	// ListChangedType.ItemAdded, ListChangedType.ItemChanged, ListChangedType.ItemDeleted, ListChangedType.ItemMoved, ListChangedType.Reset
	JobOper();
	// Add Event Handler Code
}

private void baseToolbarsManager_ToolClick(object sender, Infragistics.Win.UltraWinToolbars.ToolClickEventArgs args)
{
	switch(args.Tool.Key)
	{
	case "DeleteTool":
	case "ClearTool":
	mJobOper=string.Empty;
	break;
	}
}

}

Hmmmm not sure how to get all those codes into the inside frame.

Instead of using a ListChange event to trigger this, use a FieldChange event on the OprSeq field of the proper dataview.

Chris,

You know why I can’t call “GetQtyInfo”? I get the error indicating “Only assignment, call, increment, await, and new object expressions can only be used as statement”.

So I am using afterfieldchange to call “GetQtyInfo”, but getting error.

private void LaborDtl_AfterFieldChange(object sender, DataColumnChangeEventArgs args)
{
	// ** Argument Properties and Uses **
	// args.Row["FieldName"]
	// args.Column, args.ProposedValue, args.Row
	// Add Event Handler Code
	switch (args.Column.ColumnName)
	{
		case "OprSeq":
		GetQtyInfo;
		break;
	}
}

That is not valid syntax in C# You have to either call a function or do an assignment.

Like Jose said, that’s not valid syntax. If you dont know basic C# you’re gonna have a tough time moving forward with your customization.

One final push:

	switch (args.Column.ColumnName)
	{
		case "OprSeq":
                var myDV = oTrans.Factory("THE PROPER DATAVIEW NAME HERE");
                var job =   myDV.dataView[myDV.Row]["JobNum"].ToString();
                var asm = (int)myDV.dataView[myDV.Row]["AssemblySeq"];
                var op = (int)myDV.dataView[myDV.Row]["OprSeq"];
		GetQtyInfo(job,asm,op);
		break;
	}

To figure out what dataview you need, use Help>Field Help then select the JobNum textbox. Look at the epibinding (Table.Field). That Table name will be the name of your dataview

Thanks Chris. I got it to work now.

1 Like

Excellent - mark my response as a solution

1 Like

Thanks again. New to C# and Epicor, so this is extremely helpful.

1 Like

is possible to share your working code here?

Sure, here’s what I did to show production qty, qty completed, and remaining qty.

// **************************************************
// Custom code for StartProdForm
// Created: 4/2/2018 11:16:08 AM
// **************************************************

extern alias Erp_Contracts_BO_JobEntry;
extern alias Erp_Contracts_BO_JobAsmSearch;
extern alias Erp_Contracts_BO_JobOperSearch;
extern alias Erp_Contracts_BO_Resource;
extern alias Erp_Contracts_BO_ResourceGroup;
extern alias Erp_Contracts_BO_EmpBasic;

using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Erp.Adapters;
using Erp.UI;
using Ice.Lib;
using Ice.Adapters;
using Ice.Lib.Customization;
using Ice.Lib.ExtendedProps;
using Ice.Lib.Framework;
using Ice.Lib.Searches;
using Ice.UI.FormFunctions;

public class Script
{
// ** Wizard Insert Location - Do Not Remove ‘Begin/End Wizard Added Module Level Variables’ Comments! **
// Begin Wizard Added Module Level Variables **
// End Wizard Added Module Level Variables **

// Add Custom Module Level Variables Here **

public void InitializeCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
	// Begin Wizard Added Variable Initialization


	this.LaborDtl_Column.ColumnChanged += new DataColumnChangeEventHandler(this.LaborDtl_AfterFieldChange);
	this.baseToolbarsManager.ToolClick += new Infragistics.Win.UltraWinToolbars.ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	// End Wizard Added Variable Initialization

	// Begin Wizard Added Custom Method Calls
		
	// End Wizard Added Custom Method Calls
}


public void DestroyCustomCode()
{
	// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
	// Begin Wizard Added Object Disposal

	this.LaborDtl_Column.ColumnChanged -= new DataColumnChangeEventHandler(this.LaborDtl_AfterFieldChange);
	this.baseToolbarsManager.ToolClick -= new Infragistics.Win.UltraWinToolbars.ToolClickEventHandler(this.baseToolbarsManager_ToolClick);
	// End Wizard Added Object Disposal

	// Begin Custom Code Disposal

	// End Custom Code Disposal
}





private void LaborDtl_AfterFieldChange(object sender, DataColumnChangeEventArgs args)
{
	// ** Argument Properties and Uses **
	// args.Row["FieldName"]
	// args.Column, args.ProposedValue, args.Row
	// Add Event Handler Code

	EpiDataView edvJobOper=(EpiDataView)oTrans.EpiDataViews["Start"];
	string jobNum = edvJobOper.dataView[edvJobOper.Row]["JobNum"].ToString();
	string asmNum = edvJobOper.dataView[edvJobOper.Row]["AssemblySeq"].ToString();
	string opNum = edvJobOper.dataView[edvJobOper.Row]["OprSeq"].ToString();

	switch (args.Column.ColumnName)
	{
		case "OprSeq":

				try
				{
			             Ice.Proxy.Lib.BOReaderImpl _bor = WCFServiceSupport.CreateImpl<Ice.Proxy.Lib.BOReaderImpl>((Ice.Core.Session)oTrans.Session, Epicor.ServiceModel.Channels.ImplBase<Ice.Contracts.BOReaderSvcContract>.UriPath);
			                DataSet ds2 = _bor.GetList("Erp:BO:JobOperSearch", "JobNum = '"+jobNum+"' AND AssemblySeq = "+asmNum.ToString()+" AND OprSeq = "+opNum.ToString(), "");
				               
						 if(ds2 != null)
						{
							txtQtyCompleted.Value = ds2.Tables[0].Rows[0]["QtyCompleted"];
							txtProdQty.Value = ds2.Tables[0].Rows[0]["RunQty"];
						
						}
				txtProdQty.Text = (Convert.ToDouble(txtProdQty.Value).ToString());
				txtQtyCompleted.Text = (Convert.ToDouble(txtQtyCompleted.Value).ToString());
				double RemainingQty = (Convert.ToDouble(txtProdQty.Text)) - (Convert.ToDouble(txtQtyCompleted.Text));
				txtRemainingQty.Text = Convert.ToString(RemainingQty);
				}
				catch(Exception e)
				{
					MessageBox.Show("Error: "+e.Message);
				}
			break;
	}
}

private void baseToolbarsManager_ToolClick(object sender, Infragistics.Win.UltraWinToolbars.ToolClickEventArgs args)
{
	switch(args.Tool.Key)
	{
	case "DeleteTool":
	case "ClearTool":
	txtProdQty.Text=string.Empty;
	txtQtyCompleted.Text=string.Empty;
	txtRemainingQty.Text=string.Empty;
	break;
	}
}

}

1 Like

Thanks for sharing this code, I want to do exactly this but in in the End Activity screen (this will also be handy in the start production activity!), we are having issues with overbooking of Jobs so to be able to show the remaining Qty is a start to try and reduce this.

How easy would it be to adapt this to work in the end activity screen, I have already added in the Production Qty and Qty completed fields, adding the calculation to populate a custom field on form load is where I am now hitting a wall, I am not a coder at all so don’t know where to start.