How to stop charging time to a completed operation

Hi, I am looking to see what would be the best solution to stop users from charging time against a completed operation.

Any input or direction would be appreciated.

Hello Chantal, are you using MES? Are users failing to log out of jobs? Or what are the steps they do that results in incorrectly applied time?

If you’re at MDS in Montreal we’re just across the highway, by the way!

if you are using MES, you can create a BPM that looks at Labor.Update to find when someone is STARTING an operation… the BPM can look to see if the operation is already marked as Complete… and if it is, then disallow them from clocking in.

I use this to stop people clocking in against completed Jobs on MES, you could modify it to suit your needs
It’s on a Data Directive

var caller = callContextClient.AssemblyName;
if(caller.Contains("StartProductionActivityEntry"))
{
var jobID = ttLaborDtl.Where(l => l.Company == Session.CompanyID).Select(x => x.JobNum).FirstOrDefault();
if(jobID !=null)
{
var jobClosed = Db.JobHead.Where(j => j.Company == Session.CompanyID && j.JobNum == jobID && j.JobComplete == true).FirstOrDefault();

if(jobClosed != null)
{
throw new Ice.BLException("You cannot clock in against completed Jobs");

}}}
1 Like

Thank you for your input. We are using the Time Entry within the Time and Expense module and having users applying time against Operations that have been completed while job is still open.

@cmarleau T and E I believe uses the same labor routines as start and end activity. I know my bpms for Labor.Update also get fired there, so you could stop it on update, but the operation should have something post processing you could use to stop them before they get to update.

I would run a trace and then put an info message on the post processing I found to see if it fires. Then before you get too far put an exception with no conditions to be sure it will actually stop the transaction.

I will give it a try. thank you

We have just found the same issue. Our solution was to create a data directive on Labordtl table that prevents updates if the job operation is closed. Note: we had to make sure the BPM only fired if the laborHrs feild was changing as the Build Project Analysis process (and probably other processes) upodate the labordtl records also.

I can post details if people want.

Brett

If you can share, this would be most helpful.

Sure.

This is the BPM, its an intransaction Data Directive on the LaborDtl table.

The condition widget has the second condition line so that the BPM only fires when the LaborHrs field changes. We found that some processes would make changes to other fields so this makes sure the BPM only fires when the laborHrs has changed.

Here is the code for the custom code condition. We have a specific scenario that we need to allow people to create time entry lines with zero hours when the job operation is closed.


foreach(var l in ttLaborDtl)
{
  var job = l.JobNum;
  var OprSeq= l.OprSeq;
  var AsmSeq = l.AssemblySeq;

  if (job != string.Empty)
    {
     var opDetails = Db.JobOper.FirstOrDefault(op=> op.JobNum == job && op.AssemblySeq == AsmSeq && op.OprSeq == OprSeq);
     
     if (opDetails.OpComplete && l.LaborHrs > 0)  // throw error if the operation is complete and the labour hours are greater than zero.
       return true;
    }

}

return false;

Cheers
Brett

Thank You for sharing - I needed to display a message in MES Start Production Activity when the user entered a job number that had been marked completed but was NOT closed yet. I was able to follow your example with a few changes. I am new to BPM Custom Code and have a question for you…what exactly is the callContextClient.AssemblyName? I used what you had and it worked but curious as to what this does and if there are other call context client values - how would I find them?
Thanks Again!

Hello @dgross ,

This should get you going in the right direction

var caller = callContextClient.AssemblyName;

this.PublishInfoMessage($"Test - {caller}", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", ""); // this will show you the caller - use this to find others

if(caller.Contains("StartProductionActivityEntry")) // this is what the caller is named on the MES screen for Starting a Production activity - I put this in place because the LaborDtl table is updated in other screens where I didn't want the message to display
{
  var jobID = ttLaborDtl.Where(l => l.Company == Session.CompanyID).Select(x => x.JobNum).FirstOrDefault();
  if(jobID !=null)
  {
  
  
    bool jobInQuestion = Db.JobHead.Any(j => j.Company == Session.CompanyID && j.JobNum == jobID && j.JobComplete == true && j.JobClosed == false); // Completed not closed
    
    // bool jobInQuestion = Db.JobHead.Any(j => j.Company == Session.CompanyID && j.JobNum == jobID && j.JobClosed == true); // Closed
    
    
    if(jobInQuestion == true)
    {
    
      throw new Ice.BLException($"You cannot clock in against Job: {jobID}, it is marked as Complete"); // this is a Hard Stop
      
      // this.PublishInfoMessage($"You cannot clock in against Job: {jobID}, it is marked as Complete", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", ""); // this is a Message only
      
    }
  }
}

1 Like