High level explanation of start activity on custom screen

The work queue does a bunch of weirdo stuff I’d go with a straight up Start / End Activity… Grid or not you want the Core BO’s I’d think

1 Like

Alright, let me get that one.

To make a trace cleaner - shut down the system monitor by right clicking the icon in the system tray and choosing exit - That keeps the polling every 3 seconds or so from adding a lot of noise to the trace

5 Likes

OK, Here’s the start activity and end activity traces. Still a fair amount of stuff in each one. I’m working on getting my head around what all is being done.

And @bw2868bond, thanks for the tip about the system monitor. I forget about that one.

end activity.txt (29.6 KB)
Start activity.txt (291.8 KB)

So from what I can tell in the trace, every section like DefaultOprSeq etc, is updating the same dataset.

paramDataSet name=“ds” useDataSetNbr=“0”

So if I can populate the dataset for everything that is in the last one (update) That one should have everything that is needed for the update right?

Can I use some of the built in BO’s to get some of the information that each update section is getting? Or is it just better to build it into the query from the beginning? (like labor rate info, shift ID and that stuff)

You want to follow the trace to a T (for the most part)
So you want to make all those calls that they do, going straight to Update is generally a bad idea.
The first call to GetByID on LaborImpl() gets a hold of the current clocked in Labor record for the Employee and that returns the LaborDataSet which then is passed around to the other functions.
You would instanciate the Labor ADapter… like this and make the calls

var la = new LaborAdapter(oTrans);
la.BOConnect();
la.GetByID(123456); //123456 being the current Labor Hed Sequence for the clocked in Employee (get it via BAQ, BOReader etc)
la.StartActivity(123456,"P"); //Start Production Activity
la.DefaultJobNum("MyJobNum");
//..... etc... there are instances where you have to set the value of the data set and you can do it like this
la.LaborData.LaborDtl[0].Field="XX"; //Note that Index 0 is the first on the collection you'd have to find the right one would generally be la.LaborData.LaborDtl[la.LaborData.LaborDtl.Count-1]
la.Update();

Here is an example of creating a Labor record (Time & Expense like ) just to show you how do address fields, and how to set values and such. You’ll just mimic the same but with the right BO calls as outlined in the trace.

LaborAdapter la = new LaborAdapter(oTrans);
la.BOConnect();
LaborDataSet lds;
la.GetNewLaborHed1("1",false, DateTime.Today);
la.Update();
 
la.GetNewLaborDtlWithHdr(DateTime.Today,0,DateTime.Today,0,(int)dr["Calculated_LbrHedSeq"]);
la.DefaultJobNum(dr["JobProd_JobNum"].ToString());
la.LaborRateCalc();
la.DefaultOprSeq((int)drO["OprSeq"], out msg);
la.LaborRateCalc();
la.DefaultLaborQty(1, out msg);
la.CheckWarnings(out msg);
LaborDataSet.LaborDtlRow ld = (LaborDataSet.LaborDtlRow)lds.LaborDtl.Select("RowMod='A'")[0];
la.Update();
ld.RowMod="U";
la.ValidateChargeRateForTimeType(out msg);
la.SubmitForApproval(false, out msg)
4 Likes

ok, so just working on wrapping my head around what’s happening, so tell me if this is correct.

The labor head sequence is created when the employee clocks in right? So they should have a current one that I can pull into the BAQ using calculated fields using context data correct? So I don’t have to create a labor head record, since they are already there. So the get by ID “la.GetByID(123456);” is simply pulling in the data set from the current labor head sequence for use later. So your code is creating a data set called la, which we can use later in the code. Is that a correct explanation?

For the second chunk of code, If I was doing this in MES, I wouldn’t call the GetNewLaborHed1 right? Since there will already be one for a clocked in employee? I would start with the get by ID for the labor head sequence. Then I would do a new labor detail to create a data set (same as the “ds” set in the trace right?) Could I then adjust that data set with all of the information needed? Gathering if from the header dataset we pulled in, and data from the grid? Then I would run that dataset through each of the BO’s in the trace to check for errors, and finally run last one, Update. Is that generally what’s happening?

On a details level, I’m missing the connection of what I see in the trace, to what I do in the code.

If I see this in the trace

Erp.Proxy.BO.LaborImpl DefaultJobNum

To call that in the code, does that go like this (we’ll assume la is my dataset)

la.DefaultJobNum(expression); (I will need to learn what I need to put in the expression…)

How does it know that Erp.Proxy.BO.LaborImpl is the BO being used? Is that assumed because of the LaborAdapter? Or am I supposed to call la.BOLaborImpl(); where you have La.BoConnect();

Sorry for my ignorance on this. The two different contexts are making it harder for me to see the patterns when I’m trying to see what is related to what.

YES!

Exactly! What goes in the expression are the parameters in the trace

LaborAdapter is an implementation of the LaborImpl. LaborAdapter instanciates and maintains the LaborImpl internally. That’s what the BOConnect() does.

Right the second chunk of code was just an example , GetNewLaborHed1 creates a new LaborHed which you already have since you are clocked in.

2 Likes

That makes sense. Just for general knowledge, do can I know what BO cross references to what adapter? Is it basically just replace Impl with Adapter? Or is it more complicated than that?

Do most of the things in the dataset get handled by the BO’s? For example, clock in times, clock out times, labor rate calculations, etc. When I run the appropriate BO, it should adjust the necessary fields right? I need to get enough information to create rows on start activity, (like JobNum, AssembleSeq, Opr, and various employee info etc. The stuff that you have to input on the start activity screens), and on end activity get labor quantity data, but a large majority of the items in the data set will be modified by the BO without me having to specifically code for it right? There are 243 columns in the data set, I would hate to have to control them all.

Yes the large majority of fields happen because of the “EpiMagic” in the BO’s

3 Likes

So when I am reading the data sets in the trace, it looks like the set that is shown after the BO is the state of the data set before the BO makes any changes right? So basically (I know this is probably stupid basic, buyt I’m just learning this) Whatever shows up in the parameters section of the BO are what I need to put in the expression part of the BO call. (like was mentioned earlier in the conversation, but I didn’t make that connection until now). So for the DefaultOprSeq call, there would be 3-1 parameters, so I need 2 arguments in the code. (it looks like “CallContext” is left out?)

la.DefaultOprSeq([“OprSeq”], out msg);

This would set my operation code in the data set, right?

So I wouldn’t actually manipulate the data set directly, I would call the BO to make the adjustments and pass along the required parameters. Right?

This is starting to make a lot more sense now.

Right that’s becasue the data you see is BEFORE the method call

1 Like

So this is what I would use to call the method, correct?

1 Like

Sure

1 Like

So once I get the method calls and parameters all set up, If I were to do this with info from a grid, I would just put that in a loop to go through the grid with an “if selected” and process each one through all of the method calls, right? Set the parameters as variables from the applicable row at the beginning of the loop, process the record, then repeat for all selected rows?

I’m parsing through the trace, and there is >LaborRateCalc< in there twice, with the same parameters. Do I need to run that twice? Do I just need to follow the trace and stop asking questions? Lol.

I need to help in debugging now. I have all of the calls in the code, with the appropriate paramters, and it compiles successfully. However, it gives me an error when I try to run it.

Here’s what the error says.

Application Error

Exception caught in: mscorlib

Error Detail 
============
Message: Unable to cast object of type 'Infragistics.Win.UltraWinGrid.UltraGridCell' to type 'System.IConvertible'.
Program: CommonLanguageRuntimeLibrary
Method: ToInt32

Client Stack Trace 
==================
   at System.Convert.ToInt32(Object value)
   at Script.StartActivityButton_Click(Object sender, EventArgs args)
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at Infragistics.Win.Misc.UltraButtonBase.OnClick(EventArgs e)
   at Ice.Lib.Framework.EpiButton.OnClick(EventArgs e)
   at Infragistics.Win.Misc.UltraButton.OnMouseUp(MouseEventArgs e)
   at Ice.Lib.Framework.EpiButton.OnMouseUp(MouseEventArgs e)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

I figure it must be something so do with my parameters and variables not being the right type. The problem is, I don’t know for sure how to figure out which one it is. Should I just comment out all of the calls except the first one and keep running it until I find the offending line? Or is there a better way to figure out what is wrong? A trace doesn’t show anything, so I guess I don’t know how far it’s getting.

Here’s my full code if that helps.

// **************************************************
// Custom code for MainController
// Created: 3/12/2018 1:41:56 PM
// **************************************************
using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Ice.BO;
using Ice.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;
using Erp.BO;
using Erp.Adapters;


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 **

EpiUltraGrid WorkGrid;
int RowNum;

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

		// End Wizard Added Variable Initialization

		// Begin Wizard Added Custom Method Calls

		this.StartActivityButton.Click += new System.EventHandler(this.StartActivityButton_Click);
		// End Wizard Added Custom Method Calls
		WorkGrid = (EpiUltraGrid)csm.GetNativeControlReference("994a7055-2964-46f1-8488-0c2359109f4f");
	}

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

		this.StartActivityButton.Click -= new System.EventHandler(this.StartActivityButton_Click);
		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal
		WorkGrid = (EpiUltraGrid)csm.GetNativeControlReference("994a7055-2964-46f1-8488-0c2359109f4f");
	}

	private void StartActivityButton_Click(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **
	RowNum = -1;
	foreach(var row in WorkGrid.Rows)
		{
		RowNum = RowNum+1;
			var LHS = Convert.ToInt32(WorkGrid.Rows[RowNum].Cells["Calculated_CalcLaborHeadSeq"]);
			var EmpID = Convert.ToString(WorkGrid.Rows[RowNum].Cells["Calculated_CurrentEmpID"]);
		if(row.Selected)
			{
			var JN = Convert.ToString(WorkGrid.Rows[RowNum].Cells["JobHead_JobNum"]);
			var AS = Convert.ToInt32(WorkGrid.Rows[RowNum].Cells["JobAsmbl_AssemblySeq"]);
			var OS = Convert.ToInt32(WorkGrid.Rows[RowNum].Cells["JobOper_OprSeq"]);
			var RID = Convert.ToString(WorkGrid.Rows[RowNum].Cells["Calculated_ResourceID"]);
			var msg = "testing";
			var la = new LaborAdapter(oTrans);
			la.BOConnect();
			la.GetByID(LHS);
			la.StartActivity(LHS,"P");
			var JobNum = new JobEntryAdapter(oTrans);
			JobNum.ValidateJobNum(JN);
			la.DefaultJobNum(JN);
			la.LaborRateCalc();
			la.DefaultAssemblySeq(AS);
			la.DefaultOprSeq(OS, out msg);
			la.LaborRateCalc();
			la.CheckWarnings(out msg);
			la.CheckEmployeeActivity(EmpID,LHS,JN,AS,OS,RID, out msg);
			la.CheckFirstArticleWarning(out msg);
			la.SetClockInAndDisplayTimeMES();
			la.Update();
			}
		}
	}
}

For any of these calls where you reference Cells[“”] you need to ask for teh cell’s Value
WorkGrid.Rows[RowNum].Cells[“Calculated_CalcLaborHeadSeq”].Value

Sweet, Fixed that. Now it will clock my into an operation. I still am getting this error. It does clock me in though. That method doesn’t show in the trace, so it looks like it skips that one. Is the validate methods on typing something into a field? So should that really be on a leave field event if I were going to use that? Does that sound correct? It’s the only method that doesn’t use the labor adapter, so it seems like doesn’t belong anyways.

Application Error

Exception caught in: Erp.Adapters.JobEntry

Error Detail 
============
Message: Object reference not set to an instance of an object.
Program: Erp.Adapters.JobEntry.dll
Method: ValidateJobNum

Client Stack Trace 
==================
   at Erp.Adapters.JobEntryAdapter.ValidateJobNum(String ipJobNum)