What we’re trying to do:
When creating a new job (Quick Job Entry, or through JobEntry>GetDetails, etc…) we’d like the JobMtl.EstScrap to be filled in based on the Part that it’s being made from - i.e. 1.75" Plate Steel will consume ~30% scrap. We’re trying to go about it this way because the EstScrap doesn’t always get onto the BOM, and the EstScrap may change based on running new processes, etc.
What we’ve done so far:
Generated a UD extended field on the Erp.Part table → ScrapFactor_c, populated this field on a few test parts, and attempted to generate a method to apply this scrap factor during job creation.
The closest I’ve been able to get so far has been an In-Transaction Data-Directive for the JobMtl table - I check for added rows in the ttJobMtl table, then set the EstScrap field of the table using the ‘Update Table by Query’ setter. This will set the EstScrap field on the new job, however it doesn’t update the RequiredQty and BaseRequiredQty fields.
I feel like I need to call the ‘ChangeJobMtlEstScrap’ method to update the RequiredQty and BaseRequiredQty fields, etc. but haven’t been able to figure out how to do that.
How can I accomplish this? Am I doing this the correct way (should I be working on new entries to the JobHead table - so I have a complete tableset, then update the JobMtl table on that tableset and call the ChangeJobMtlEstScrap method from there)?
We are using MoM’s however we produce a lot of repeat parts and a lot of new ‘one-off’ parts, and to enable those methods to use the most recent scrap factor for the material they’re consuming, wouldn’t we need to modify all of those MoM’s to take the new scrap rate into account?
This is what we are trying to avoid - We are trying to reduce the extra task of setting the EstScrap when we generate the Methods (as well as remembering/looking up what the current rate is for each Mtl).
Every job will need to ‘Get Details’ to pull the method to the Job, so this seemed like the ideal place to accomplish this. However, since some of this seems to occur ‘behinds the scenes’ based on how the job was created, we starting looking for other ways to accomplish this - like whenever a part is added to a job (hence the JobMtl data directive).
Perhaps I’m having a difficult time explaining here (or a difficult time understanding - sleep deprivation may be at play) - I can get the EstScrap data onto the job when materials are added/changed on the JobMtl table through Job Entry or Quick Job Entry, I just can’t get the RequiredQty fields to update with the new multiplier… It felt like I was really close to what I was after here.
FWIW: I’ve been playing with trying to do this with the JobEntry.ChangeJobHeadJobEngineered event as suggested, however I can’t get any changes to happen. I can tell that the event is firing (used a test message in my BPM), then tried filling all rows of the dsJobMtl.EstScrap fields with a specific number, but nothing changes on the job. Do I need to call something to push the changes in the ds Tables to the database here? When using the ‘Update Table by Query’ setter, I’m setting the RowMod to U along with my part.scrapfactor_c field binding, and that’s not showing up in the traces either.
Seems like I’m missing something simple here (apart from sleep) - is there any way to dump the contents of the dsJobMtl during BPM processing to verify that there’s actually something in the table to work with?
So, I’ve managed to get the EstScrap set to a static amount and recalculate the related fields in code (found out that I needed to call the ChangeJobMtlEstScrap function for each row individually) - current code below:
string JobNumber = null;
var Job = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobEntrySvcContract>(Db);
var JobTs = (Erp.Tablesets.JobEntryTableset)ds;
JobNumber = ds.JobHead.FirstOrDefault().JobNum;
if (JobNumber !=null)
{
JobTs = Job.GetByID(JobNumber);
foreach(var Matl in JobTs.JobMtl)
{
Matl.EstScrap = 25;
Matl.RowMod = "U";
Job.ChangeJobMtlEstScrap(ref JobTs);
Job.Update(ref JobTs);
}
}
I’d tried using the ‘ds’ from the JobEntryChangeJobHeadJobEngineered post-processing directive, but there didn’t seem to be any data populated in the ds.JobMtl table to query against, hence the code above…
What I’m now having trouble with is bringing in my Part.scrapfactor_c field to populate the EstScrap field in my code. I’m not seeing too many examples of joining a table to a table within a Tableset.
We will work out the ‘When’ to apply the function once I have a working function to apply - if that makes sense.
FWIW: I have gotten it working based on a JobMtl Standard Data-Directive (currently testing in our sandbox environment) Thanks @Mark_Wonsil and @jkane - your input did get me to look at other functions and adjusted my GoogleFu a bit to find the answers I was looking for.
Code below, for those interested. I’m open to any assistance in optimizing this code (constructive criticism also welcome). The mystring variable and debug notification will go away when we move it into production.
Code first verifies that the job actually exists and is not closed, then tries opening a new Tableset for that job. Then iterates through the ttJobMtl for Added/Updated rows that haven’t had Mtl Issued, and verifies that the EstScrap is current. If not, it opens that row in the JobMtl table, updates the EstScrap and RowMod fields, calls the ChangeJobMtlEstScrap method, and Updates the job.
Referencing Assembly:
Erp.Contracts.BO.JobEntry
Usings:
Using Ice.Assemblies;
Using Erp.Proxy.BO;
Using Erp.BO;
Using Erp.Contracts;
Using Erp.Tablesets;
// ** Update EstScrap fields on changed to JobMtl table for Open Jobs
Erp.Tables.Part Part;
string mystring = "Job Number: \n";
string JobNumber = ttJobMtl.FirstOrDefault().JobNum; // Grab and verify JobNum
if (JobNumber != null)
{
var JobHd = (from JobHead_row in Db.JobHead // Verify Job exists and that it's open
where JobHead_row.JobNum == JobNumber
&& JobHead_row.JobClosed == false
select JobHead_row).FirstOrDefault();
if (JobHd != null)
try // Try Opening Job in new TableSet
{
var Job = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobEntrySvcContract>(Db);
Erp.Tablesets.JobEntryTableset JobTS = new Erp.Tablesets.JobEntryTableset();
JobTS = Job.GetByID(JobNumber);
mystring += JobNumber + "\n\n";
foreach (var Matl in ttJobMtl) // Verify that added or updated rows have correct EstScrap
{
if (Matl.IssuedQty == 0 && (Matl.RowMod == "A" || Matl.RowMod == "U")) // issued matl and only A and U rows?
{
Part = (from Part_row in Db.Part
where Part_row.PartNum == Matl.PartNum
select Part_row).FirstOrDefault();
decimal ScrapFactor = Part.UDField<decimal>("ScrapFactor_c"); // Grab current scrap factor from Part table
if (Matl.EstScrap != ScrapFactor) // If wrong Est Scrap - attempt to change it in JobTs
{
mystring += Matl.IssuedQty + Matl.PartNum + " - " + Matl.EstScrap + " -> " + ScrapFactor + "\n";
var MatlToUpd = (from JobMatl_row in JobTS.JobMtl // ** Select Row to change
where JobMatl_row.JobNum == Matl.JobNum
&& JobMatl_row.AssemblySeq == Matl.AssemblySeq
&& JobMatl_row.MtlSeq == Matl.MtlSeq
select JobMatl_row).FirstOrDefault();
if (MatlToUpd != null) // Found matching record?
{
MatlToUpd.EstScrap = ScrapFactor; // ** Change Row's EstScrap
MatlToUpd.RowMod = "U";
Job.ChangeJobMtlEstScrap(ref JobTS); // ** Call ChangeJobMtlEstScrap function
Job.Update(ref JobTS); // ** Call Job Update
}
}
}
}
} catch (Exception ex) { // notify on errors
this.PublishInfoMessage(ex.Message, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");
}
}
this.PublishInfoMessage(mystring, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", ""); // present results to user