Completed code to clock out workers, include stop for related activity tasks

To whom it could be interesting.
Here is completed code to clock out workers, include stop for related activity tasks. There are couple functions, which are working directly with EPICOR data base, to select (GetAllWorkers2ClockOut and GetWorkerRelatedActivitiesDtl). This way is much faster compare with standard (adapter) way.

********
 try 
{
	Log.LogMessageToFile("\n***CheckClockOutWorkers is using " + ((bDevelop) ? " E101Test.sysconfig" : "E101Live.sysconfig"));
	bret = CheckClockOutWorkers4Company(bDevelop, strCompany) 
}
catch(Exception ee)
{
	Log.LogMessageToFile("\nException: " + ee.Message);
	return false; 
}
********

private bool CheckClockOutWorkers4Company(bool bDevelop, string strCompany)
{
	bool bret = true ;
	try
	{
		Log.LogMessageToFile("\n **CheckClockOutWorkers4Company, CompanyID: " + strCompany);
		List<string> Workers2Clockout = GetAllWorkers2ClockOut(bDevelop, strCompany);
		if (Workers2Clockout != null && Workers2Clockout.Count > 0)
		{
			Ice.Core.Session currentEPICORsession = GetEpicorWorkingSession(bDevelop);
			if (currentEPICORsession != null)
			{
				currentEPICORsession.CompanyID = strCompany;
				Ice.Lib.Framework.ILauncher EPI_trans = new Ice.Lib.Framework.ILauncher(currentEPICORsession);
				if (EPI_trans != null)
				{
					Erp.Adapters.EmpBasicAdapter Emp_Adapter = new Erp.Adapters.EmpBasicAdapter(EPI_trans);
					if (Emp_Adapter != null)
					{
						if (Emp_Adapter.BOConnect())
						{
							foreach (string strIDT in Workers2Clockout)
							{
								bret &= (EndAllActivities(bDevelop, EPI_trans, strIDT, strCompany) && ClockOutOneWorker(Emp_Adapter, strIDT)) ? true : false;
							}

							Emp_Adapter.Dispose();
						}
					}
				}
				currentEPICORsession.Dispose();
			}
		}
		else
		{
			Log.LogMessageToFile("\nNO ClockIn workers found");
		}
	}
	catch(Exception ee)
	{
		Log.LogMessageToFile("\nCheckClockOutWorkers4Company exception: " + ee.Message);
		return false;
	}
	return bret;
}


private bool EndAllActivities(bool bDevelop, Ice.Lib.Framework.ILauncher EPI_trans, string strIDT, string strComp)
{
	bool bret = true;
	try
	{
		List<LaborSeq> Obj2EndActivity = GetWorkerRelatedActivitiesDtl(bDevelop , strIDT, strComp);
		if (Obj2EndActivity != null && Obj2EndActivity.Count > 0)
		{
			foreach (LaborSeq LabrSeqT in Obj2EndActivity)
			{
				Log.LogMessageToFile("\nSelected to end activity. Worker id ->" + strIDT+", JobNum->"+ LabrSeqT .JobNumber+ " , LaborHedSeq->" + LabrSeqT.LaborHedSeq + " , LaborDtlSeq ->" + LabrSeqT.LaborDtlSeq);
				Erp.Adapters.LaborAdapter Lbr_Adapter = new Erp.Adapters.LaborAdapter(EPI_trans);
				if (Lbr_Adapter != null)
				{
					if (Lbr_Adapter.BOConnect())
					{
						if (Lbr_Adapter.GetDetail(LabrSeqT.LaborHedSeq, LabrSeqT.LaborDtlSeq))
						{
							DataTable lDtlT3 = Lbr_Adapter.LaborData.LaborDtl;
							if (lDtlT3 != null && lDtlT3.Rows.Count > 0)
							{
								lDtlT3.Rows[0]["LaborQty"] = 0;
								lDtlT3.Rows[0]["ScrapQty"] = 0;
								lDtlT3.Rows[0]["SetupPctComplete"] = 0;
								lDtlT3.Rows[0]["ActiveTrans"] = 0;

								if (Lbr_Adapter.EndActivity() )
								{
									Log.LogMessageToFile("Activity ended. Worker id ->" + strIDT + ", JobNum->" + LabrSeqT.JobNumber + " , LaborHedSeq->" + LabrSeqT.LaborHedSeq + " , LaborDtlSeq ->" + LabrSeqT.LaborDtlSeq);
									lDtlT3.Rows[0]["RowMod"] = "U";
									if (!Lbr_Adapter.Update())
										bret = false;
								}
								else
								{
									bret = false;
									Log.LogMessageToFile("\nWorker -> " + strIDT + " FAILED to END ACTIVITY, LaborHedSeq->" + LabrSeqT.LaborHedSeq + " , LaborDtlSeq ->" + LabrSeqT.LaborDtlSeq);
								}
							}
						}
					}
					else
					{
						Log.LogMessageToFile("\nWorker -> " + strIDT + " cannot get db data Lbr_Adapter.BOConnect()");
						bret = false;
					}
					Lbr_Adapter.Dispose();
					Thread.Sleep(5);
				}
				else
				{
					Log.LogMessageToFile("\nWorker -> " + strIDT + " cannot create Erp.Adapters.LaborAdapter");
					bret = false;
				}
			}
		}
		else
		{
			Log.LogMessageToFile("\nWorker -> " + strIDT + " do not have any opened activities.");
		}		
	}
	catch (Exception ee)
	{
		Log.LogMessageToFile("\nClockInOneWorker for " + strIDT + " worker, exception: " + ee.Message);
		return false;
	}
	return bret;
}

private bool ClockOutOneWorker(Erp.Adapters.EmpBasicAdapter Emp_Adapter, string WorkerID)
{
	try
	{
		if (Emp_Adapter.GetByID(WorkerID))
		{
			string strRef = WorkerID;
			Emp_Adapter.ClockOut(ref strRef);
			Log.LogMessageToFile("\nWorker -> " + WorkerID + " successfully clockout");
			return true;
		}
		else
			Log.LogMessageToFile("\nClockInOneWorker, Emp_Adapter.GetByID(WorkerID) returns FALSE for " + WorkerID + " worker");
	}
	catch (Exception ee)
	{
		Log.LogMessageToFile("\nClockInOneWorker for "+ WorkerID + " worker, exception: " + ee.Message);
		return false;
	}

	return false;
}

private List<string> GetAllWorkers2ClockOut(bool bDevelop, string strCompany)
{
	try
	{
		DbWorker db = new DbWorker();
		return db.GetAllWorkers2ClockOut( bDevelop,  strCompany);
	}
	catch(Exception ee)
	{
		Log.LogMessageToFile("\nGetAllWorkers2ClockOut exception: " + ee.Message);
		return null;
	}
}

private List<LaborSeq> GetWorkerRelatedActivitiesDtl(bool bDevelop, string strIDT, string strCompany)
{
	try
	{
		DbWorker db = new DbWorker();
		return db.GetWorkerRelatedActivitiesDtl(bDevelop, strIDT,  strCompany);
	}
	catch (Exception ee)
	{
		Log.LogMessageToFile("\nGetWorkerRelatedActivitiesDtl exception: " + ee.Message);
		return null;
	}
}

public class LaborSeq
{
	public int LaborDtlSeq = 0;
	public int LaborHedSeq = 0;
	public string JobNumber = String.Empty;
}

Thank you for the example. Some questions:

  1. Could you upload the code as an attachment file. Its not very clear where some routines begin and end as the code scrolls in some areas of the post.

  2. Are you running this as a scheduled process? If so could you elaborated on how this was accomplished.

Regards,

sorry, my mistake

clockout_workers.cs (5.4 KB)

yes. I made it like windows form .exe app, and I am ruining it from scheduled process at EPICOR server

Did you have to use Epicor SDK to do this? Also, I assume you are using Epicor Task Agent scheduler and not Windows Scheduler?

I did use Windows Schedule service to start everyday at 23:30 the clock out application. Looks like it most easiest way:slightly_smiling_face:
I did try to use EPICOR schedule to trigger some event , and connect to custom code. But limit of time force me to use Windows Scheduler.

1 Like

seems this code missing two methods, class 1. GetEpicorWorkingSession, 2. DbWorker class

This is something I would like to use but can you excuse the noobish developer question.

Where in Epicor 10 do I load or import the code so that I can then schedule it?

here is, you need to change it for your environment accordingly.
private Ice.Core.Session GetEpicorWorkingSession(bool bDevMod )
{
string strServersConnect = (bDevMod)? “\\EPICOR_SERVER_DEV\ERP10.1.600\Client\config\E101.sysconfig” : “\\EPICOR_SERVER\ERP10.1.600\Client\config\E101.sysconfig”;
try
{
return new Ice.Core.Session(“123manager”, “123manager”, Ice.Core.Session.LicenseType.DataCollection, strServersConnect);
}
catch(Exception ee)
{
Log.LogMessageToFile("\nGetEpicorWorkingSession exception: " + ee.Message);
return null;
}
}

1 Like

here is example of DB class, it should be workable

public class DbWorker
{
    protected string strEPICOR = "EPICORDB";
    protected string strEPICORTEST = "EPICORDBTEST";
    protected string strUSER = "123manager";
    protected string strPWD = "123manager"; 

    public List<string> GetAllWorkers2ClockOut(bool bDevelop, string strCompany)
    {
        string strSQL = "Select distinct lh.EmployeeNum,lh.Shift,lh.ClockInDate,lh.ClockInTime, lh.ClockOutTime  FROM ERP.LaborHed as lh  where lh.Company = '" + strCompany + "' AND lh.ClockInDate = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) and lh.Clockouttime = 0 and lh.ClockInTime <> 0";
        try
        {
            if(bDevelop)
                Log.LogMessageToFile("\n"+strSQL);

            DataTable tablRES = null;
            using (SqlConnection conn = new SqlConnection(StrDbConnect(bDevelop)))
            {
                SqlCommand cmd = new SqlCommand();
                cmd.CommandText = strSQL;
                cmd.CommandType = CommandType.Text;
                cmd.Connection = conn;
                conn.Open();
                SqlDataReader reader = cmd.ExecuteReader();
                if (tablRES == null)
                    tablRES = new DataTable();

                tablRES.TableName = "TEMP";
                tablRES.Load(reader);
                conn.Close();

                List<string> ret = null;
                if (tablRES.Rows.Count > 0)
                {
                    ret = new List<string>();
                }

                for (int i = 0; i < tablRES.Rows.Count; i++)
                {
                    ret.Add(tablRES.Rows[i].ItemArray[0].ToString());
                }
                return ret;
            }
        }
        catch (Exception ee)
        {
            Log.LogMessageToFile("GetAllWorkers2ClockOut exception: " + ee.Message);
            return null;
        }
    }


    public List<LaborSeq> GetWorkerRelatedActivitiesDtl(bool bDevelop, string strIDT, string strCompany)
    {
        string strSQL = " SELECT DISTINCT ld.LaborHedSeq, ld.LaborDtlSeq , ld.JobNum FROM Erp.LaborDtl as ld ";
        strSQL += " where ld.EmployeeNum = '" + strIDT + "' and ld.Company = '" + strCompany + "' and ld.ActiveTrans = 1 and ClockOutTime in (0, 24.00) ";
        strSQL += " and ld.LaborHedSeq in ";
        strSQL += "(SELECT distinct lh.LaborHedSeq FROm Erp.LaborHed as lh WHERE lh.EmployeeNum = '" + strIDT + "' and lh.Company = '" + strCompany + "' and lh.ActiveTrans = 1 and lh.ClockOutTime = 0 )";

        try
        {
            if (bDevelop)
                Log.LogMessageToFile("\n" + strSQL);

            DataTable tablRES = null;
            using (SqlConnection conn = new SqlConnection(StrDbConnect(bDevelop)))
            {
                SqlCommand cmd = new SqlCommand();
                cmd.CommandText = strSQL;
                cmd.CommandType = CommandType.Text;
                cmd.Connection = conn;
                conn.Open();
                SqlDataReader reader = cmd.ExecuteReader();
                if (tablRES == null)
                    tablRES = new DataTable();

                tablRES.TableName = "TEMP";
                tablRES.Load(reader);
                conn.Close();

                List<LaborSeq> ret = null;
                if (tablRES.Rows.Count > 0)
                    ret = new List<LaborSeq>();

                foreach (DataRow drT in tablRES.Rows)
                {
                    LaborSeq objT =new LaborSeq();
                    objT.LaborDtlSeq = drT.Field<int>("LaborDtlSeq");
                    objT.LaborHedSeq = drT.Field<int>("LaborHedSeq");
                    objT.JobNumber = drT["JobNum"].ToString();
                    ret.Add(objT);
                }
                return ret;
            }
        }
        catch (Exception ee)
        {
            Log.LogMessageToFile("GetWorkerRelatedActivitiesDtl exception: " + ee.Message);
            return null;
        }
    }


    private string StrDbConnect(bool bDevelop)
    {
        return string.Format(@"Data Source=SQLSERVERNAME;Initial Catalog={0}; User ID={1}; Password={2}; Trusted_Connection=False", ((bDevelop)? strEPICORTEST : strEPICOR), strUSER, strPWD);
    } 



}
1 Like
  1. You need to create WinApp.EXE, something like this. There is nothing at the form, but only one timer. CMainWorker, explained at the beginning.

namespace ClockInWorkers
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

    private void timer1_Tick(object sender, EventArgs e)
    {
        timer1.Stop();
        Log.LogEmptyStrToFile();
        Log.LogMessageToFile("\n--------- Clock In/Out Workers started ---------");
        if(CheckClockOut())
            Log.LogMessageToFile("\n--------- Clock In/Out Workers finished successfully ---------");
        else
            Log.LogMessageToFile("\n--------- Clock In/Out Workers finished with errors ---------");

        Close();
    }

    private bool CheckClockOut()
    {
        try
        {
            CMainWorker obj = new CMainWorker();
            if(obj.CheckClockOutWorkers())
                return true;

        }
        catch(Exception ee)
        {
            Log.LogMessageToFile("\nClock In/Out exception: "+ee.Message);
            return false;
        }

        return false;
    }
}

}

  1. Compile all, and copy all from debug, or release into specific directory at your EPICORSERVER computer.

  2. At EPICORSERVER computer, you need to find TaskScheduler application.
    Run it.
    Inside opened meny, create new task. Set schedule for this task.
    Assign full path to your winApp .exe file.

sorry not been on the forum for a few days. Thanks for replying. I will give that a try

Is it possible to run this code from a BPM or scheduled process from within Epicor?

Hi,

Another option to consider is, If you have DMT you can write a Powrshell script that can be scheduled.

The script runs a BAQ that identifies all those logged and outputs the data to CSV. The script then runs on and then runs a DMT load in update mode that logs those users that are logged in, out.

Cheers,

Andrew.

<#
.SYNOPSIS
<This script calls a BAQ at
2358 every night to return all employees logged onto the system>
.DESCRIPTION
Two calls via DMT, on generates a list of employees still clocked on
This uses the DSEBAQClockOut BAQ
The file is generated in the c:\temp directory
The file is used as the source file a command line import on the ‘TE Labor Combined’
upload
.PARAMETER <Parameter_Name>
None
.INPUTS
$DMTPath - location of the DMT path
$User - dmt upload user
$Pass - dmt upload password
$Env =- location of the config file
$DMTLoad - name of the DMT load (needs 2 quotes to allow for spaces in the name)
$Config - name of the config file
$Source - file name of the CSV generated by the BAQ
$Log - names of the log file
$completeLog - name of the completed log file
.OUTPUTS
Log file stored in c:\temp\ClockOutEmps.csv.CompleteLog.txt
.NOTES
Version: 1.0
Author: Andrew Clements
Creation Date: 10/07/2017
Purpose/Change: Initial script development

#>

#Extract Data From From BAQ -> CSV File -> Load in with DMT

$DMTPath = “C:\Epicor\ERP10.0Client\Client\DMT.exe”
$User = “manager”
$Pass = “*****”
$Env = "net.tcp://EPICORAPP/EpicorERPPilotUN”
$DMTLoad = “TE Labor Combined”
$Config = “EpicorERPPilotUN”

$Source = “C:\Powershell\ClockOutEmps.csv”
$Log = “C:\Powershell\ClockOutEmps.log”
$completeLog = “C:\Powershell\ClockOutEmps.csv.CompleteLog.txt”

Write-Output “Extracting Data via BAQ $(get-date)”

Start-Process -Wait -FilePath $DMTPath -ArgumentList “-ConnectionUrl $Env -User $User -Pass $Pass -Export -BAQ BAQClockOut -Target $Source -NoUI -ConfigValue $Config”
Write-Output "Loading Data $(get-date) " $Source

#Load Data $
Start-Process -Wait -FilePath $DMTPath -ArgumentList “-ConnectionUrl $Env -User $User -Pass $Pass -Add=false -Update=true -Import ““TE Labor Combined”” -Source $Source -NoUI -ConfigValue $Config -logfile $Log”

#Check Results
select-string -Path $completeLog -Pattern “Records:\D*(\d+/\d+)” -AllMatches | % { $.Matches.Groups[0].Value }
select-string -Path $completeLog -Pattern “Errors:\D*(\d+)” -AllMatches | % { $
.Matches.Groups[0].Value }

3 Likes

Hi @aclements
Thanks for the info on this

What are you actually returning with your DSEBAQClockout BAQ to pass to the “TE Labor Combined” DMT?

I’m guessing something as it relates to all LaborDtl records with ActiveTrans=1 , but how are you manipulating that dataset to be a clean DMT to actually end the activity on the labor record? Can you supply this BAQ or the columns you’re pulling?

We’ve managed to end the production activity using the below DMT but it’s not updating the LaborHrs / BurdenHrs dynamically based on ClockInDate/Time (just returns 0) – also not sure if I’m missing other pieces for this to be a clean close out.

Thanks again! Any help would be appreciated

-Mike

Hi Mike,

Firstly welcome to the forum.

The process runs @ 23.58 every night and runs the following query.

select 
	[LaborHed].[Company] as [LaborHed_Company],
	[LaborHed].[EmployeeNum] as [LaborHed_EmployeeNum],
	[LaborHed].[LaborHedSeq] as [LaborHed_LaborHedSeq],
	(23.98) as [Calculated_ClockOutTime],
	('false') as [Calculated_ActiveTrans]
from Erp.LaborHed as LaborHed
where (LaborHed.ClockOutTime = 0)

I’m afraid I can’t post the BAQ export due to ip issues.

It must be noted that this does not handle open labordtl records and would leave them active.

The scope of this requirement, was that the client is using the clock in / clock out function as a time clock for payroll and also as a way of determining who is on site for a fire safety roll call, so they could not have people remaining clocked on whilst off site, so i didn’t need to consider open labordtl records.

If you wanted to do this with all the ‘bells and whistles’ , then I would be looking at creating an executable in Visual Studio, that called the Epicor ‘Labor.EndActivity’ and ‘EmpBasic.ClockOut’ methods.

You could then schedule this executable to run at a given time, using the Microsoft scheduler.

Andrew.

Do you have a copy of your BAQ?