Hi everyone, I am trying to write a simple standard data directive on the JobOper table. I want to be able to trap jobs that are created with either a zero qtyPer or a zero prodStd. The exceptions being if the operations are ‘Issue’, ‘SubDes’ or ‘Despatch’, as I expect these operations to have zero in the prodStd. The BPM then sends an email with the jobs that need correcting. My problem is that the BPM is not ignoring the three operations I want it to and emails me every job created. What am I doing wrong here?
Thanks for your help.
Adrian.
if i were you, i will create a pre-process method BPM on releasing Jobs, this way you will capture every job before hitting the shop floor, i have done many BPM’s on this BO method as well as EWB, the reason for that is that Epicor create jobs in a massive scale when running the MRP process in my environment, so i decided to capture these jobs at this point/transaction to allow only jobs with times, approved Supplier ID, sub.con. days out values to go to the shop floor, i can share any with you and you can alter to suit your use if you want
Thanks for your reply Al, we have not been Live for a year yet. I used to spend all my time writing the configurators. I am now trying to iron out some of the problems in production. We have quite a few! I’m ok but far from experienced with BPMs so any help / advice you can give would be much appreciated.
The HasNoStdCost? is a Custom Condition which returns true / false
/* Check JobAsmbl and JobMtl MINUS REFD */
var ttJobHead_Row = ttJobHead.FirstOrDefault();
/* JobAsmbls with No PartCost StdCost and Class not REFD */
bool hasJobAsmblsWithNoStdCost =
(from ja in Db.JobAsmbl.With(LockHint.NoLock)
/* Join on PartNum to get ClassID */
join p in Db.Part.With(LockHint.NoLock)
on new { ja.Company, ja.PartNum } equals new { p.Company, p.PartNum }
join pl in Db.Plant.With(LockHint.NoLock)
on new { ja.Company, PL = ja.Plant } equals new { pl.Company, PL = pl.Plant1 }
join pc in Db.PartCost.With(LockHint.NoLock)
on new { pl.Company, CID = pl.PlantCostID, PartNum = ja.PartNum } equals new { pc.Company, CID = pc.CostID, PartNum = pc.PartNum }
where
(( ja.Company == ttJobHead_Row.Company && ja.JobNum == ttJobHead_Row.JobNum )) &&
(p.ClassID != "REFD") &&
(pc.StdLaborCost + pc.StdBurdenCost + pc.StdMaterialCost + pc.StdSubContCost + pc.StdMtlBurCost) == 0
select ja
).Any();
/* JobMtls with No PartCost StdCost and Class not REFD */
bool hasJobMtlsWithNoStdCost =
(from jm in Db.JobMtl.With(LockHint.NoLock)
/* Join on PartNum to get ClassID */
join p in Db.Part.With(LockHint.NoLock)
on new { jm.Company, jm.PartNum } equals new { p.Company, p.PartNum }
join pl in Db.Plant.With(LockHint.NoLock)
on new { jm.Company, PL = jm.Plant } equals new { pl.Company, PL = pl.Plant1 }
join pc in Db.PartCost.With(LockHint.NoLock)
on new { pl.Company, CID = pl.PlantCostID, PartNum = jm.PartNum } equals new { pc.Company, CID = pc.CostID, PartNum = pc.PartNum }
where
(( jm.Company == ttJobHead_Row.Company && jm.JobNum == ttJobHead_Row.JobNum )) &&
(p.ClassID != "REFD") &&
(pc.StdLaborCost + pc.StdBurdenCost + pc.StdMaterialCost + pc.StdSubContCost + pc.StdMtlBurCost) == 0
select jm
).Any();
return hasJobAsmblsWithNoStdCost || hasJobMtlsWithNoStdCost;
no problem mate, this is my BPM to check setup and production time for each operation on JobOper except our MATCHECK, the BPM will display a message to worn user then set JobReleased check box back to false, i have designed it this way to avoid cater for Firm, Engineered/ and UnEng transactions then run the update method, you can alter it to send you an Email instead if you want,
the code as text:
Erp.Tables.JobHead JobHead;
Erp.Tables.JobOper JobOper;
string sAllOpFlag = string.Empty;
var ttJobHead_xRow = (from ttJobHead_Row in ttJobHead
where ttJobHead_Row.Company == Session.CompanyID
&& ttJobHead_Row.RowMod == "U"
select ttJobHead_Row).FirstOrDefault();
if (ttJobHead_xRow != null)
{
foreach (var JobOper_iterator in (from JobOper_Row in Db.JobOper
where JobOper_Row.Company == Session.CompanyID
&& JobOper_Row.JobNum == ttJobHead_xRow.JobNum
&& JobOper_Row.SubContract == false
&& JobOper_Row.EstSetHours == 0.00m
&& JobOper_Row.EstProdHours == 0.00m
&& JobOper_Row.OpCode != "MATCHECK"
select JobOper_Row))
if (JobOper_iterator != null)
{
sAllOpFlag ="Op. Seq. No."+JobOper_iterator.OprSeq+" : "+JobOper_iterator.OpCode+"_"+sAllOpFlag;
}
}
if (sAllOpFlag != "")
{
PublishInfoMessage("The following Operations: " + sAllOpFlag + " have no Estimated Setup nor Production Time", Ice.Common.BusinessObjectMessageType.Warning, Ice.Bpm.InfoMessageDisplayMode.Individual, "JobEntry","ChangeJobHeadJobReleased");
}
@A.Baeisa modify your post and add this around your code block
```cs
yourcode
```
many thanks @hkeric.wci
Looks like million dollar code now
Thank you Haso and Al, It’s such a relief to find that there are people here who have done pretty much the same as I am trying to do and are willing to help. I will have a go at adjusting your code and let you know how I get on.
Thanks again guys
Adrian.
Mine sents a custom Email with the Entire Job with the ones that have issues in Red in a HTML Table which is Mobile Friendly to a point.
Now that is very cool indeed Haso
Given that I used a Template provided by the Community, I guess it should not be a problem to re-share it.
// Zero Cost Job Part Email
//
// 12/06/17 HK: Initial Implementation, since this is considered a temporary, lets keep it simple
//
// Initialize Actions
Func<string, string> GetCompanyAddressAction = (CompanyID) => {
var Company_Row =
(from sc in Db.SysCompany.With(LockHint.NoLock)
where sc.Company == CompanyID
select new { sc.EmailFromAddr, sc.EmailFromLabel }).FirstOrDefault();
if (Company_Row != null) {
return string.Format(@"""{0}"" <{1}>", Company_Row.EmailFromLabel.Trim(), Company_Row.EmailFromAddr.Trim());
}
return string.Empty;
};
// Initialize Variables
decimal TotalStdCost = 0;
// Initialize Mail Variables
string EmailTO = "chrissconnwhereareyou@aol.com";
//string EmailTO = "youcanAddMultiples@cc.me; orccMe@whitehouse.org";
string EmailCC = "";
string EmailBCC = ""; // If blank itll be just ignored on bottom
string EmailSubject = "";
string EmailBody = "";
// Get JobHead
var ttJobHead_Row = ttJobHead.FirstOrDefault();
// Get Demand Link Details
var JobProd_Row =
(from jp in Db.JobProd.With(LockHint.NoLock)
join jh in Db.JobHead.With(LockHint.NoLock)
on new { jp.Company, JN = jp.TargetJobNum } equals new { jh.Company, JN = jh.JobNum }
where
jp.Company == ttJobHead_Row.Company && jp.JobNum == ttJobHead_Row.JobNum && jp.PartNum == ttJobHead_Row.PartNum
select new {
jp.Company, jp.Plant, RecordType = "JobProd", jp.TargetJobNum, jp.TargetAssemblySeq, jp.TargetMtlSeq,
DemandPartNum = jh.PartNum
}
).FirstOrDefault();
if (ttJobHead_Row != null)
{
// Set Subject
EmailSubject = "[ Alert ] Missing Std Cost - Job #: " + ttJobHead_Row.JobNum + " Released in Company " + ttJobHead_Row.Company;
// Start Email Body
EmailBody += string.Format("Job # <b>{0}</b> was released in Company <b>{1}</b> on <b>{2}</b> by <b>{3}</b>:",
ttJobHead_Row.JobNum, ttJobHead_Row.Company, DateTime.Today.ToString("d"), callContextClient.CurrentUserId);
EmailBody += "<BR><BR>";
EmailBody +=
@"<table width='100%' align='center' border='1' cellspacing='1' cellpadding='1' style='font-family: Arial; font-size: 10pt;' bordercolor='#CCCCCC'>"
+ "<tr>"
+ "<th>Demand Job / Part</th>"
+ "<th>Job / Asm / Mtl</th>"
+ "<th>Part Type</th>"
+ "<th>Part</th>"
+ "<th>Prod Group</th>"
+ "<th>Class</th>"
+ "<th>Qty</th>"
+ "<th>Std Cost</th>"
+ "</tr>";
// Union JobAsmbl and JobMtl since they have common columns needed for Details Query
var JobParts = (
from ja in Db.JobAsmbl.With(LockHint.NoLock)
where ja.Company == ttJobHead_Row.Company && ja.JobNum == ttJobHead_Row.JobNum
select new {
ja.Company, ja.Plant, RecordType = "JobAsmbl", Seq = ja.AssemblySeq, ParentSeq = ja.Parent, ja.JobNum, ja.PartNum, ja.Description, ja.IUM, ja.RequiredQty
}
).Union(
from jm in Db.JobMtl.With(LockHint.NoLock)
where jm.Company == ttJobHead_Row.Company && jm.JobNum == ttJobHead_Row.JobNum
select new {
jm.Company, jm.Plant, RecordType = "JobMtl", Seq = jm.MtlSeq, ParentSeq = jm.AssemblySeq, jm.JobNum, jm.PartNum, jm.Description, jm.IUM, jm.RequiredQty
}
);
// Get The Rest of Details for JobParts
var JobParts_Rows =
(from jp_rows in JobParts
join pl in Db.Plant.With(LockHint.NoLock)
on new { jp_rows.Company, PL = jp_rows.Plant } equals new { pl.Company, PL = pl.Plant1 }
join pc in Db.PartCost.With(LockHint.NoLock)
on new { pl.Company, CID = pl.PlantCostID, PartNum = jp_rows.PartNum } equals new { pc.Company, CID = pc.CostID, PartNum = pc.PartNum }
/* Join on PartNum to get ClassID */
join p in Db.Part.With(LockHint.NoLock)
on new { jp_rows.Company, jp_rows.PartNum } equals new { p.Company, p.PartNum }
/* Left Join on PartClass, just incase it does not exist - To Get Description */
join pcl in Db.PartClass.With(LockHint.NoLock)
on new { p.Company, p.ClassID } equals new { pcl.Company, pcl.ClassID }
into pclj
from pcl in pclj.DefaultIfEmpty()
/* Left Join on ProdGrup, just incase it does not exist */
join prdc in Db.ProdGrup.With(LockHint.NoLock)
on new { p.Company, p.ProdCode } equals new { prdc.Company, prdc.ProdCode }
into prdcj
from prdc in prdcj.DefaultIfEmpty()
select new {
jp_rows.Company, jp_rows.Plant, jp_rows.RecordType, jp_rows.Seq, jp_rows.ParentSeq, jp_rows.JobNum, jp_rows.PartNum, jp_rows.Description, jp_rows.IUM, jp_rows.RequiredQty,
p.ClassID, ClassDescription = pcl != null ? pcl.Description : "",
p.ProdCode, ProdGroupDesc = prdc != null ? prdc.Description : "",
TotalStdCost = (pc.StdLaborCost + pc.StdBurdenCost + pc.StdMaterialCost + pc.StdSubContCost + pc.StdMtlBurCost)
}).ToList();
Ice.Diagnostics.Log.WriteEntry("[ ZeroCostJobPart Notification ] - Building Lines...");
foreach (var JobPart_Row in JobParts_Rows)
{
TotalStdCost = Math.Round(JobPart_Row.TotalStdCost, 3);
EmailBody +=
"<tr style='" + (TotalStdCost == 0 && JobPart_Row.ClassID != "REFD" ? "color: red;" : "") + "'>"
+ @"<td align='center'>" + (JobProd_Row == null ? " - " : JobProd_Row.TargetJobNum + " / " + JobProd_Row.DemandPartNum) + "</td>"
+ @"<td align='center'>" + JobPart_Row.JobNum + " / " + JobPart_Row.ParentSeq + " / " + JobPart_Row.Seq + "</td>"
+ @"<td align='center'>" + JobPart_Row.RecordType + "</td>"
+ @"<td align='center'><b>" + JobPart_Row.PartNum + @"</b><br><span style='font-size: 8pt'>" + JobPart_Row.Description + "</span></td>"
+ @"<td align='center'><b>" + JobPart_Row.ProdCode + @"</b><br><span style='font-size: 8pt'>" + JobPart_Row.ProdGroupDesc + "</span></td>"
+ @"<td align='center'><b>" + JobPart_Row.ClassID + @"</b><br><span style='font-size: 8pt'>" + JobPart_Row.ClassDescription + "</span></td>"
+ @"<td align='center'>" + Math.Round(JobPart_Row.RequiredQty, 2) + " " + JobPart_Row.IUM + "</td>"
+ @"<td align='center'>" + Math.Round(JobPart_Row.TotalStdCost, 2) + "</td>"
+ "</tr>";
}
EmailBody += "</table>";
Ice.Diagnostics.Log.WriteEntry("[ ZeroCostJobPart Notification ] - Sending Email...");
// Send Email
var mailer = this.GetMailer(async:true);
var message = new Ice.Mail.SmtpMail();
message.SetFrom( GetCompanyAddressAction(ttJobHead_Row.Company) );
message.SetTo(EmailTO);
message.SetCC(EmailCC);
message.SetBcc(EmailBCC);
message.SetSubject(EmailSubject);
message.SetBody(EmailBody);
message.IsBodyHtml = true;
mailer.Send(message);
Ice.Diagnostics.Log.WriteEntry("[ ZeroCostJobPart Notification ] - Email Sent!");
}
i think that you need to start teaching your fancy stuff and revealing your secrets in coding, i have been looking for such LINQ techniques for a long time, if you do i will be your first student mate.♂
I will join you Al
so it does have C#LINQ (Database and UI) forms functions and techniques ?
Yes it has a lets solve this problem via C#… then it has a … Now lets solve it via LINQ Only. It has a decent amount of problems to show you the power of LINQ and a diff approach to problem solving. It comes with LINQPad Examples you can embed into LINQPad and tinker w/.
you are a star
I got my copy on order