Help with BPM custom code for MES End Activity

We want to stop shop users from end labor and entering a quantity on a job if non-backflushed material has not yet been issued. I have managed to piece together some custom code That does a good job of this. It halts entry of quantity greater than zero and list the parts that haven’t been issued. It will return an informational message if the user end labor with quantity zero and will return a confirmation message if all parts have been issued.
My problem is this seems to work with every operation on a job. I’d really like it to only work with the last operation but I’m not sure how to incorporate that into my code.
In practice many of our jobs have multiple operations e.g. Pick, Paint & Assemble. You could log into and complete the Pick and Paint operations without having all material issued but you shouldn’t be allowed to complete the Assemble operation.
Below is my code. I welcome any ideas. TIA

var insufficientMaterialFlag = false;
var insufficientMaterialsList = new List<string>();

foreach (var labor in ds.LaborDtl)
{
    if (labor.StartActivity) continue;

    if (!labor.EndActivity && !(labor.EndActivity && labor.LaborQty == 0)) continue;

    var jobMtls = (from row in Db.JobMtl
                   where row.Company == labor.Company && row.JobNum == labor.JobNum && row.AssemblySeq == labor.AssemblySeq && row.RelatedOperation == labor.OprSeq
                   select row).ToList();

    foreach (var mtl in jobMtls)
    {
        // Check IssuedComplete and BackFlush
        if (!mtl.BackFlush && !mtl.IssuedComplete)
        {
            insufficientMaterialFlag = true;
            insufficientMaterialsList.Add(mtl.PartNum); // Add part to the list
        }
    }
}
foreach (var labor in ds.LaborDtl)
if (insufficientMaterialFlag)
{
    if (labor.EndActivity && labor.LaborQty > 0)
    {
        var message = $"Entry not allowed. The following parts have not been issued completely to the job: {string.Join(", ", insufficientMaterialsList)}. Please check the IssuedComplete and BackFlush fields for the related materials.";
        throw new Ice.BLException(message);
    }
    else
    {
        var message = $"The following parts have not been issued completely to the job: {string.Join(", ", insufficientMaterialsList)}. Please check the IssuedComplete and BackFlush fields for the related materials.";
        this.PublishInfoMessage(message, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");
    }
}
else
{
    this.PublishInfoMessage("All required material has been issued. Proceed with quantity entry.", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");
}

You can check if it’s the last operation similar to what you’re doing for start activity, and ending with 0. Look at all of the operations of the job, and if “Final operation” is checked, or it’s the operation with the highest sequence on the job, then continue.

Then, when you pick your list of job materials, remove your related operation to get all of the materials within an assembly.

I think that’s all that you should have to do.

Personally though, I like what you have with the related operation being the material that issued to that operation. If you don’t have the materials related to the correct operation, then I would fix that. If I need parts A and B for op 10, check those at op 10. If I need C at op 20m check it at op 20.

That’s what you have there, I believe.

Thanks. I think the Final Operation field is in the JobOper table which, sadly, I don’t have linked in my code. I didn’t mention that we auto receive everything to inventory upon completion of the final operation which is why I’m looking for my stop to be there.
I’m not strong at code but I think I just need to outer join the JobOper table on Company and JobNum to the LaborDtl table. If I can do that then I should be able to limit the code from acting on any op that’s not the final op.
I’d like to just limit the code firing by the BPM conditions but I don’t think that table is available. I’ll have to dig deeper.

It’s actually in the JobAsm table.

Currently, it should only be firing for materials that are related to the operation that is being reported. Is that the case?

Yes, you’re correct, it is in JobAsm table.
The code is not looking for the related operation, it is just looking for the JobMtl.IssuedComplete field to be true unless the BackFlush field is true, then it ignores the part. We no longer tie the parts to related operation as we manually issue parts to jobs. We do still backflush things like raw material, hardware, expense items, etc. I modified another code (with some help from ChatGPT) for this BPM and that may be why it’s still in there.

row.RelatedOperation == labor.OprSeq

This line is filtering by related operation.

Well, if that’s not that code you’re working with, it’s kind of hard to help you…

I just tested it again and yes, it is filtering by op sequence (how did I miss that?). What I meant was I started with another code and added/subtracted from it to build this code. Now I have a bigger problem. My code is ONLY looking at part material by sequence number. It ignores parts without a related op. I don’t think I can get Engineering to change their ways so I will have to look for all materials not backflushed where IssuedComplete = true.
And have the exception stop entering quantity on the final op if parts haven’t been issued. I thought I was so close too.

Personally as a bit of advice, I would change were the code is being invoked from:

I think I could better placed:
Erp.BO.Labor → DefaultOprSeq → post processing

This is the call being made when selecting the operation from the dropdown in the start activity screen, so essentially your stoping the labor record from its initial creation.

There in the custom code you could:

  1. Declare your variable for the returned dataset
    var lbrRow = ds.LaborDtl.FirstOrDefault();

  2. Null check and break our
    If (lbrRow == null) return; //break out of block

  3. Bool validation for your materials scenario, for all mtls for asm and prior and current op.

bool jobMtls = Db.JobMtl.Where(r.Company == lbrRow.Company && r.JobNum == lbrRow.JobNum && r.AssemblySeq == lbrRow.AssemblySeq && r.RelatedOperation <= lbrRow.OprSeq && !r.backflush && !r.IssueConplete).Select(r => r).Any();

  1. If bool jobMtls is true

If (jobMtls)
{
//throw your error and convert list to error message

}

Apologies in advance this has been written on my phone :slight_smile:

1 Like