How to run the schedule JOB via a BAQ / BPM

I did a updatable BAQ on the job head start date…
it’s working , but I need to run the schedule job and pass the parameters…

using the job entry form, the schedule job will popup… to run the sched job…

but in the BAQ , need to force it…
did the BPM method on JobHead, but new to the C# coding with Epicor… and not sure how to address this…
did a trace and got this, but not sure how to code this…

hope someone can help me…

businessObject>Erp.Proxy.BO.ScheduleEngineImpl
MoveJobItem
https://cadcentraldtpilot03.epicorsaas.com/SaaS530Pilot/
System.Void
12/19/2024 11:05:21:3636293
88
cfccefff-4037-410a-9adf-20c8fbcb52d4

0




160529
000733-1-1
0
0
0
2025-02-26T11:05:10-05:00
0
2025-02-18T00:00:00-05:00
0
false
false
JJ
Start
false
false
false
2
false
false
false
false
false
072723ee-6fd0-4f9b-a94d-8eb1bcf4e82b
A






00000000-0000-0000-0000-000000000000




adding a screen shot

Welcome Robert! You are diving right into the deep end!
oof… Scheduling via BPM is not for the faint of heart. There are so many methods called and secret sauce injected, it is hard to reproduce the epimagic for this particular task.

First you need Jose’s trace parser to help make sense of the traces (you dont strictly NEED it, but it helps!). Trace Helper Utility for Epicor ERP 10 - Epicor ERP 10 - Epicor User Help Forum

Next run your trace again, and reproduce the methods you want to trace. If you are scheduling a job, turn on tracing before you open Job Entry. This should give you a more complete view of all the methods and the parameters they used.

Next you need to start reproducing these methods in a BPM. I often find that we need to use GetDatasetForTree, before I call any methods, then I have to use an update table by query to make changes to the data before finally doing the update method. Here is a recent BPM example where both of the above are needed: How to unengineer a job using a BPM? - Epicor ERP 10 - Epicor User Help Forum

Good luck!
Nate

1 Like

It’s been a minute, but I’ve dabbled in the ScheduleEngineSvc. The one example I got into production actually uses ChangeResource() instead of MoveJobItem(). I don’t recall why. I vaguely recall there being fewer issues.

This sits in a function, so syntax will be a bit different than straight BPM. Lightly edited to make it a bit more clear what’s going on.

//Gets the current time, rounds it to the beginning of the minute. Used for start time of op.
TimeSpan sinceMidnight = DateTime.Now.AddSeconds(-DateTime.Now.Second)
                                - DateTime.Today;
int startTime = (int)(sinceMidnight.TotalSeconds);
string warn;

var row = new Erp.Tablesets.ScheduleEngineRow();
row.Company = CompanyID;
row.JobNum = YourJob;
row.AssemblySeq = YourAssmSq;
row.OprSeq = YourOprSq;
row.OpDtlSeq = sched.OpDtlSeq;
row.StartDate = BpmFunc.Today();
row.StartTime = startTime; //This is seconds from midnight.
row.SchedTypeCode = "oo"; //Operation Only. There's options for moving whole jobs or assemblies too.
row.ScheduleDirection = "Start"; //Enter 'End' to backwards schedule from your time.
row.ResourceGrpID = YourResourceGrpID;
row.ResourceID = YourResource;
row.AllocNum = YourAllocNum; 
row.UpdateJobOpDtl = true; //I don't think this does anything if you disallow editing of engineered jobs.
row.RowMod = "A"; //for *A*dded row

var SchedEngTs = new Erp.Tablesets.ScheduleEngineTableset();
SchedEngTs.ScheduleEngine.Add(row);

CallService<Erp.Contracts.ScheduleEngineSvcContract>(svc =>
{
    svc.SetMachines(SchedEngTs);
    SchedEngTs.ScheduleEngine.FirstOrDefault().ResourceID = Resource;
  
    svc.ChangeResource(SchedEngTs,
                       SchedEngTs.ScheduleEngine.FirstOrDefault().AllocNum,
                       Resource,
                       false
                       ,out warn);
    svc.AcceptChanges("", SchedEngTs);
});

Regardless of which method you call, the gist is going to be the same: instantiate a ScheduleEngineTableset. Populate a ScheduleEngineRow (most of the options within it should be vaguely familar to you if you’ve spent any time moving jobs around). Add row to table. Call a method to move the item. Then call AcceptChanges (another action that should be familiar to scheduling users).

2 Likes

@NateS is right, you are diving into the deep end! But if you read through all the threads here (many with him on it!), you can eventually figure out everything you need. After lurking many posts here I achieved it with C# Code Widget in an Epicor Function, but I no longer have that code.

Below is a function for scheduling a job that I do have, but it is in JavaScript. It might be helpful though to see the data that needs sent:

//all params are strings.. @runShift should be "day" or "night" @runDay is MM/DD/YYYY format
  async scheduleJob(jobNum, runDay, runShift, resourceID, resourceGrpID) {
    const runDate = new Date(runDay);

    if (isNaN(runDate)) {
      throw new Error("Invalid runDay format. Use 'MM/DD/YYYY'.");
    }

    const shiftStartTime = runShift === "day" ? 60 * 60 * 7 : 60 * 60 * 15;
    const shiftEndTime = shiftStartTime + 60 * 60 * 8;

    const startDate = new Date(runDate.getTime() + shiftStartTime * 1000).toISOString();
    const endDate = new Date(runDate.getTime() + shiftEndTime * 1000).toISOString();

    const scheduleEngineDataSet = {
      ScheduleEngine: [
        {
          Company: this.parent.getCompany(),
          JobNum: jobNum,
          AssemblySeq: 0,
          OprSeq: 10,
          OpDtlSeq: 0,
          StartDate: startDate,
          StartTime: shiftStartTime,
          EndDate: endDate,
          EndTime: shiftEndTime,
          WhatIf: false,
          Finite: false,
          SchedTypeCode: "JA",
          ScheduleDirection: "START",
          SetupComplete: false,
          ProductionComplete: false,
          OverrideMtlCon: true,
          OverRideHistDateSetting: 2,
          ResourceID: resourceID,
          ResourceGrpID: resourceGrpID,
          RecalcExpProdYld: false,
          UseSchedulingMultiJob: false,
          SchedulingMultiJobIgnoreLocks: false,
          SchedulingMultiJobMinimizeWIP: false,
          SchedulingMultiJobMoveJobsAcrossPlants: false,
          RowMod: "A",
        },
      ],
    };

    const endpoint = `Erp.BO.ScheduleEngineSvc/MoveJobItem()`;
    return await this.parent.fetchApi(endpoint, "POST", { ds: scheduleEngineDataSet });
  }

I pulled this from a JS Class for dispatching unfirm jobs from MRP to a selected resource on day/night shift (for the first operation which was a fixed hours operation), so you will need to adjust this for your use case. What I found in my webapp was I needed to do a few things (engineering/unengineering) to achieve everything I wanted. Here was the order that worked for me:

  • (1) Unengineer job
  • (2) Update job start date / required date
  • (3) Assigning Resource ID
  • (4) Engineer job
  • (5) Schedule job

I just bring that up in case you happen to run into more problems. But if you are just scheduling and the job is already engineered, then I think what I posted is enough. If you do need more, and you think it would be helpful I can post the full JS Class which has all the endpoints/data needed for all the steps I listed above.

1 Like

thanks for the tip

i was able to do it… with functions… and it’s doing what I want …

in case someone is looking for something similar…
adding the function…
again… thanks a lot

//Gets the current time, rounds it to the beginning of the minute. Used for start time of op.
TimeSpan sinceMidnight = DateTime.Now.AddSeconds(-DateTime.Now.Second)- DateTime.Today;
int startTime = (int)(sinceMidnight.TotalSeconds);
string warn;

var row = new Erp.Tablesets.ScheduleEngineRow();
row.Company = Session.CompanyID;
row.JobNum = iJobNum;
row.AssemblySeq = 0;
row.OprSeq = 0;
row.OpDtlSeq = 0;
row.StartDate = iStartDate;
row.StartTime = startTime; //This is seconds from midnight.
row.EndDate = iStartDate;
row.EndTime = startTime + (8 * 60 * 60); //This is seconds from midnight.
row.WhatIf = false;     // false.
row.Finite = false;     // false.
//row.SchedTypeCode = "oo";       //Operation Only. There's options for moving whole jobs or assemblies too.
row.SchedTypeCode = "JJ";         // from trace
row.ScheduleDirection = "Start";  //Enter 'End' to backwards schedule from your time.
row.SetupComplete = false;
row.ProductionComplete = false;
row.OverrideMtlCon = false;
row.OverRideHistDateSetting = 2;
row.RecalcExpProdYld = false;
row.UseSchedulingMultiJob = false;
row.SchedulingMultiJobIgnoreLocks = false;
row.SchedulingMultiJobMinimizeWIP = false;
row.SchedulingMultiJobMoveJobsAcrossPlants = false;
//row.ResourceGrpID = YourResourceGrpID;
//row.ResourceID = YourResource;
//row.AllocNum = YourAllocNum; 
//row.UpdateJobOpDtl = true;      //I don't think this does anything if you disallow editing of engineered jobs.
row.RowMod = "A";                 //for *A*dded row

var SchedEngTs = new Erp.Tablesets.ScheduleEngineTableset();
SchedEngTs.ScheduleEngine.Add(row);

CallService<Erp.Contracts.ScheduleEngineSvcContract>(svc =>
{
       //svc.MoveJobItem(SchedEngTs);
        svc.MoveJobItem(SchedEngTs, out lFinish, out lWarn);
        
        svc.AcceptChanges("", SchedEngTs);
 });
1 Like

Hello, i am trying to do something very similar. I am very new at writing custom code in BPMs so this might be a stupid question, but when I put “out lFinish, out lWarn” in the MoveJobItem parameters I get these errors
“The name lFinish does not exist in the current context”
“The name lWarn does not exist in the current context”

do i have to define this somewhere else? or how do i fix this? and what do these parameters do?

I figured out what i was missing.