Change Resource Group ID for Open Jobs

Thanks to Jose Gomez for helping out over on the other post. I can now run custom actions through my customization code!

Now onto the final hurdle! I need to get my custom action working properly. As I said above, the goal of this custom code is to change the Resource Group ID and Description fields to the values specified by the user. These values get passed along to the BAQ though call context BPM data elements. The values get passed along alright, but the custom action doesn’t do what I want.

So I have modified the code a bit.

foreach (var xRow in (from ttResults_Row in ttResults select ttResults_Row))
{
  MyJob = xRow.JobHead_JobNum;
  MyAsm = xRow.JobAsmbl_AssemblySeq;
  MyOp = xRow.JobOper_OprSeq;
  
  Erp.Tables.JobOpDtl JobOpDtl;
  Erp.Tables.JobOper JobOper;
  using (var txscope1 = IceDataContext.CreateDefaultTransactionScope())
   {
     var op = (from opRow in Db.JobOpDtl where opRow.JobNum == MyJob && opRow.AssemblySeq == MyAsm && opRow.OprSeq == MyOp select opRow);
     if (op != null)
        {    
          op.ResourceGrpID = NewResGrpID;
          op.OpDtlDesc = NewResGrpDesc;
          Db.Validate(op);
        }
     var oper = (from operRow in Db.JobOper where operRow.JobNum == MyJob && operRow.AssemblySeq == MyAsm && operRow.OprSeq == MyOp select operRow);
     if (oper != null)
      {
        oper.OpDesc = NewResGrpDesc;
        Db.Validate(oper);
      }
      txscope1.Complete();
   }
}

This won’t compile and returns:

‘IQueryable’ does not contain a definition for ‘ResourceGrpID’ and no accessible extension method ‘ResourceGrpID’ accepting a first argument of type ‘IQueryable’ could be found (are you missing a using directive or an assembly reference?)

‘IQueryable’ does not contain a definition for ‘OpDtlDesc’ and no accessible extension method ‘OpDtlDesc’ accepting a first argument of type ‘IQueryable’ could be found (are you missing a using directive or an assembly reference?)

The type ‘System.Linq.IQueryable<Erp.Tables.JobOpDtl>’ cannot be used as type parameter ‘TLinqRow’ in the generic type or method ‘IceDataContext.Validate(TLinqRow)’. There is no implicit reference conversion from ‘System.Linq.IQueryable<Erp.Tables.JobOpDtl>’ to ‘Ice.LinqRow’.

‘IQueryable’ does not contain a definition for ‘OpDesc’ and no accessible extension method ‘OpDesc’ accepting a first argument of type ‘IQueryable’ could be found (are you missing a using directive or an assembly reference?)

The type ‘System.Linq.IQueryable<Erp.Tables.JobOper>’ cannot be used as type parameter ‘TLinqRow’ in the generic type or method ‘IceDataContext.Validate(TLinqRow)’. There is no implicit reference conversion from ‘System.Linq.IQueryable<Erp.Tables.JobOper>’ to ‘Ice.LinqRow’.

Here is my interpretation of what should be happening:

  1. The foreach statement loops through all the records in the query.
  2. For each record in the query we pull down the current jobnum, asmseq, and opseq. This should be enough to identify the records in the underlying tables.
  3. Next we open up the JobOpDtl table and try to update the ResourceGrpID, and OpDtlDesc for any record that matches the current record from the foreach loop.
  4. Next we open up the JobOper table and try to update the OpDesc for any record that matches the current records from the foreach loop.
  5. Finally, we close the transaction, having made the appropriate updates.

Am I on the right track here? Am I way off?
Thanks so much for taking time to read and consider a solution!
Nate

I have considered the issue overnight. I believe that the op and oper variables are not being set correctly. I think that I need to alter the SQL statements to join in the jobhead, jobasmbl, joboper, and jobopdtl tables.

I am a bit fuzzy on my SQL and this is a strange dialect for me. I will keep trying to brute force the syntax. :hammer:

I have a few questions.

Is this new to you? (i don’t want to assume and walk you through something you may have done already)
The two fields NewResGrpID and NewResGrpDesc are they for each line, or is this a mass update?
Where are the two fields on the user form?

This is all a bit new to me, though I have been programming various languages for years. Adapting them all to Epicor has been tough. You all have been a great help!

The ID and description are set at the form level. The user types in a value for both fields (eventually drop down boxes). Then any open job that has ‘12VA’ as a resource group ID, for any operation, will be replaced with ‘4A’.

for testing I have disconnected the callcontextbpmdata elements and I am just working on the BPM code. So for now I just hard coded in the two values. But in the final run they will be passed in the BPM using the two set argument elements. That code will eventually be:

callContextBpmData.ShortChar01
//and
callContextBpmData.Character01

Does that make sense?

So the BAQ brings back all jobs that need to be updated.
User updates a few fields on the form.
Clicks Save

BPM fires
You want to find each of the jobs from the query and update the two fields.

The path you are going you will bypass the BO logic of Epicor? As the code loops through each record of the BAQ and tries to update the database directly with the field data from the form.

Yes that about sums it up. The only reason I am trying to bypass the BOs is because they didn’t work. See this old thread:

I get that same error when I try to use the JobEntry method ChangeJobOpDtlResourceGrpID.

Based on the information in the last post I think I am up against the same internal bug as Matthew. I have updated my code as follows and I get the same problem he did. I also commented out the ChangeJobOpDtlResourceGrpID method and tried to manually update the fields. That gave me a list of interesting errors like:

Cannot manually update the Production Labor Rate in JobOper.
Cannot manually update the Production Burden Rate in JobOper.
Cannot manually update the Setup Labor Rate in JobOper.
Cannot manually update the Setup Burden Rate in JobOper.

This custom code produced the errors above:

Erp.Contracts.JobEntrySvcContract jobEntryBO = null;
jobEntryBO = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobEntrySvcContract>(Db);

foreach (var row in ttResults)
{
  {
    myCount = myCount + 1;
    MyJob = row.JobHead_JobNum;
    MyAsm = row.JobAsmbl_AssemblySeq;
    MyOp = row.JobOper_OprSeq;
    var jobTableset = new JobEntryTableset();
    jobTableset = jobEntryBO.GetByID(row.JobHead_JobNum);
     
    var jobOpDtl = jobTableset.JobOpDtl;
    var jobOper = jobTableset.JobOper;
  
    var jobOpDtlRow = (from dtlRow in jobOpDtl where (dtlRow.Company == Session.CompanyID) && (dtlRow.AssemblySeq == row.JobAsmbl_AssemblySeq) && (dtlRow.OprSeq == row.JobOper_OprSeq) select dtlRow).FirstOrDefault();
    
    var jobOperRow = (from dtlRow in jobOper where (dtlRow.Company == Session.CompanyID) && (dtlRow.AssemblySeq == row.JobAsmbl_AssemblySeq) && (dtlRow.OprSeq == row.JobOper_OprSeq) select dtlRow).FirstOrDefault();
    
    jobOperRow.RowMod = "U";
    jobOperRow.OpDesc = NewResGrpDesc;
  
    jobOpDtlRow.RowMod = "U";
    jobOpDtlRow.ResourceGrpID = NewResGrpID;
    jobOpDtlRow.OpDtlDesc = NewResGrpDesc;
   // jobEntryBO.ChangeJobOpDtlResourceGrpID(NewResGrpID, ref jobTableset);
    
    jobEntryBO.Update(ref jobTableset);
    
  }
}

Well, the BO doesn’t work the way we want it to. There’s probably a good reason why the Epicor BO is not allowing a direct update of the Resource. Epicor may be making changes in other places of the database that you are not aware of. Maybe Capacity Planning? Costing? Capability Planning? We just don’t know. That’s why it’s best to work through the BOs the way the Client would and then automate from there instead of trying to get around it.

Have you run a trace of how to change a Resource Group on an Operation from the client?

I was afraid of that. That is a good reason.
I guess I just have to wait until they fix the BO?

I ran my trace on the JobEntry form. I manually found a job with ‘12VA’ an then started the trace, updated the code to ‘4A’, and then read the trace.

I assume you don’t want 15,000 lines of trace data. So the potentially relevant methods from my trace are:
Update
ChangeJobOpDtlResourceGrpID
CheckInactiveSchedRequirement
GetDatasetForTree (this probably isn’t relevant)

These types of methods are usually checks between lookup and the update. Sometimes they just check for errors, sometimes they do the change in your dataset (so you don’t have to in code).

I’m assuming that your list isn’t in order?

1 Like

The exact order of JobEntry BO methods are:

  • ChangeJobOpDtlResourceGrpID
  • CheckInactiveSchedRequirement
  • Update
  • GetDatasetForTree

I would rerun that trace, but start with a blank screen. Have all of your lookup values handy so you can typed them in instead of searching for them (because you won’t need the search stuff) But you will need a dataset populated, and your list methods doesn’t have anything that does that. So that dataset was populated before you started the trace.

That’s where Jose and Josh’s Trace Helper Utility comes into play!

It should help you identify only the methods that you’ll need to replicate the steps you want to do.

3 Likes

I would create a BPM

Use the BO widgets
GetJobByID
Change resource id field
ChangeJobOpDtlResourceGrpID
Update the RowMod
Update

What @Mark_Wonsil said!!

Yes! I got that utility this morning and I already love it! That is actually how I am getting the list of methods to use. I will try to use all BO widgets instead of doing it all in code.

I did modify the code to try to use just the methods:

Erp.Contracts.JobEntrySvcContract jobEntryBO = null;
jobEntryBO = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobEntrySvcContract>(Db);

foreach (var row in ttResults)
{
  {
    myCount = myCount + 1;
    MyJob = row.JobHead_JobNum;
    MyAsm = row.JobAsmbl_AssemblySeq;
    MyOp = row.JobOper_OprSeq;
    var jobTableset = new JobEntryTableset();
    jobTableset = jobEntryBO.GetByID(row.JobHead_JobNum);
     
   
    jobEntryBO.ChangeJobOpDtlResourceGrpID(NewResGrpID, ref jobTableset);
    jobEntryBO.CheckInactiveSchedRequirement("", "", NewResGrpID, out myString);
    jobEntryBO.Update(ref jobTableset);
    
  }
}

This returns a new error:

Business Layer Exception

Operation Detail is not available.

Exception caught in: Epicor.ServiceModel

Error Detail

Description: Operation Detail is not available.
Program: Erp.Services.BO.JobEntry.dll
Method: ChangeJobOpDtlResourceGrpID
Line Number: 11770
Column Number: 17
Table: JobOpDtl

Thanks all for your time and valuable feedback! I’ll keep trying!

1 Like

try and do this with the widget. at least then you will know what is needed for the call.

1 Like

And it will probably upgrade better as well.

There has to be something missing in that process. The GetByID returns everything on the job. Then the update is only the job data set and the whole job. Something has to be set somewhere so that the method knows which resource ID (AsmSeq, and OpSeq) to change.

Just putting my two cents in, but I know when we manually change a resource on a scheduled job, it causes various things to happen IF that job is not removed from the schedule and then re-scheduled. Make sure you can do this or you will run into this.

Global scheduling may handle it but how often do you want to run it?

1 Like