Stop Employee Clocking to Operation Unless Previous Operation was Completed - Help Needed

We have been asked to develop a way to stop employees from clocking into job operations if a prior operation is not marked complete. I was thinking on creating it using a bpm, but am unsure where to start. Any tips would be greatly appreciated.

1 Like

I am no pro with method directives, but I think you are looking at setting up a method directive. I would start by targeting this method: Erp.Proxy.BO.LaborImpl.DefaultOprSeq()

If you can tap into this method, say in the pre-processing, then I think you could do your check to see if the previous op has been completed. I have a BAQ that looks at the job number and operation that you give it, then it pulls out the previous op and tells you if it has been completed. Take a look and use what you can from it!

Good luck!
FindPrevOp.baq (30.4 KB)


@dgreenEA pre processing on Labor.Update is where I stop operators from doing a start activity. Mine is to make sure you are in the right resource group for the operation, but the same process.

(ttLaborDtl_Row.Added() || ttLaborDtl_Row.Updated()) && ttLaborDtl_Row.ActiveTrans == true && ttLaborDtl_Row.LaborType == "P"))

read JobOper for JobNum, AssemblySeq, equal to ttlaborDtl and OprSeq < ttlaborDtl.OprSeq
OrderBy JobNum, AssemblySeq ascending and OprSeq descending
with FirstOrDefault this will get you the previous Operation.

You only need the OpComplete to come back or you could add OpComplete and use .Any to set a bool.


Hi, maybe not the best help, but it would be a starting point, you can take a glance on this, this allows employee to clock in but does not allow to report greater QTY than has been done on previous operation.
It is built on Labor.Update PreProcessing

int OpBefore = 0; //Operation before dataset
int OpBeforeOp = 0; //OpSeq before dataset
int OpBeforeAsembly = 0;//AssemblySeq before dataset
decimal OpBeforeQty = 0;//QtyCompleted before dataset
string OpBeforeOpCode = ""; //OpCode before dataset
decimal PrevQty = 0; //Previous quantity of current operation before dataset
decimal ReportQty =0;//Current ds qty+laborqty before dataset
decimal TempQty = 0;//dataset quantity sum

foreach(var dsRow in (from dsRows in ds.LaborDtl where dsRows.Company == "XXX" && (dsRows.RowMod == IceRow.ROWSTATE_ADDED || dsRows.RowMod == IceRow.ROWSTATE_UPDATED) && dsRows.LaborType == "P" && dsRows.TimeStatus == "E" && dsRows.ActiveTrans == true select dsRows)) //Loop thrue dataset
  var List = (from rows in Db.JobOper.With(LockHint.NoLock) where rows.Company == dsRow.Company && rows.JobNum == dsRow.JobNum && rows.AssemblySeq == dsRow.AssemblySeq && rows.OprSeq < dsRow.OprSeq  select rows.OprSeq).ToList(); //get previous OprSeq or 0
  OpBefore = List.LastOrDefault();

  foreach(var row in Db.JobOper.With(LockHint.NoLock).Where(a=>a.Company == dsRow.Company && a.JobNum == dsRow.JobNum && a.OprSeq == OpBefore)) //Get op before data
    OpBeforeOp = row.OprSeq;
    OpBeforeAsembly = row.AssemblySeq;
    OpBeforeQty = row.QtyCompleted;
    OpBeforeOpCode = row.OpCode;
   foreach(var row in Db.LaborDtl.With(LockHint.NoLock).Where(r=>r.Company == dsRow.Company && r.JobNum == dsRow.JobNum && r.AssemblySeq == dsRow.AssemblySeq && r.OprSeq == dsRow.OprSeq && r.OpCode == dsRow.OpCode && r.ActiveTrans != true)) //Get current operation sum of LaborQty
     PrevQty = PrevQty + row.LaborQty;
   TempQty = TempQty + dsRow.LaborQty; //Get sum of dataset qty's
   ReportQty = TempQty + PrevQty; //get current report qty


  if(OpBeforeOp != 0 ) //If previous operations exist
    if(ReportQty > OpBeforeQty )
      throw new Ice.Common.BusinessObjectException(new Ice.Common.BusinessObjectMessage("(BPM:Op_Qty_Check) " + "You can not report this qty becouse: " + "Previous Op  " + OpBeforeOpCode + " reported qty is: " + Convert.ToInt32(OpBeforeQty) + " and total QTY on this Op is: " + Convert.ToInt32(ReportQty))  
      Type = Ice.Common.BusinessObjectMessageType.Error,

A post-processing method directive on Labor.DefaultOprSeq should do it with the following custom code:

Erp.Tables.JobOper JobOper;

// Find active record   
  foreach (var ttLaborDtl_i in (from ttLaborDtl_Row in ttLaborDtl
              select ttLaborDtl_Row))
  var ttLaborDtlRow = ttLaborDtl_i;

// Select associated Job Operation record
JobOper = (from JobOper_Row in Db.JobOper
             where ttLaborDtlRow.Company == JobOper_Row.Company
           && ttLaborDtlRow.JobNum == JobOper_Row.JobNum
           && ttLaborDtlRow.AssemblySeq == JobOper_Row.AssemblySeq
           && ttLaborDtlRow.OprSeq == JobOper_Row.OprSeq
           //&& ttLaborDtlRow.OpComplete == JobOper_Row.OpComplete
            select JobOper_Row).FirstOrDefault();

    var PrevOp = (from JobOper_Row in Db.JobOper
              where JobOper_Row.OprSeq
              < ttLaborDtlRow.OprSeq && JobOper.OpComplete == false
              select JobOper_Row);
    if (PrevOp != null)
                CallContext.Current.ExceptionManager.AddBLException("Previous operation not complete");



One thing to keep in mind: when you do this on the post-processing of Labor.DefaultOprSeq as Sue points out, you get the message earlier in the process - meaning when the user changes the Operation Sequence and tabs out. If you catch it on the Update method, you’ve allowed the user to fill out the entire screen before you’re giving them the error. I prefer the DefaultOprSeq method - consider it a bit more user friendly.

If you use both Start/End production activity as well as Report Quantity, make sure you test both programs. I think the method directives fire in both, so shouldn’t be an issue. But you definitely don’t want to assume.

Kevin Simon

1 Like