How to remove What-If Schedule?

I have been trying to utilize the What-if schedule feature. As a result, I have set what-if scheduling on a lot of jobs (working in Pilot). I am now left wondering if there is an easy way to undo the what-if scheduling on all jobs at once? Does Global Scheduling remove any previous what-if?
Thanks!
Nate

Global will clear it. What-If and the standard schedule are stored in the ResourceTimeUsed table. Every job has two records in there, regular schedule and what-if. Global resets the deck. I believe when you move jobs around before saving/commiting in scheduling boards these are modifying the what-ifs

2 Likes

Perfect! Thanks a bunch!

Running Global Scheduling does work to clear out the WI schedule, but is there a quicker easier way to do it? I would like to give my manager the ability to load up a WI schedule, review the impact, and then dump it to load in a new WI schedule. I can’t ask him to wait 30+ minutes while global scheduling runs. Besides we didn’t actually change nay schedules, so there’s no need to global schedule.

I think I could accomplish this with a DMT load, but I am hopeful there is another way. Does anyone know a quicker way to clear out the What-if schedule?
Thanks!
Nate

You can just use “Undo All Changes” in whatever scheduling board your manager is using. If your user can’t unwind their changes quickly enough with that, I’d suggest they should probably touch fewer things in one go or be willing to wait the 30 mins for the full flush.

1 Like

What if, I created the what-if schedule using a BPM in my own custom dashboard? Do you know if there’s a way to add that undo all changes to a custom dashboard? Maybe I can just open the job scheduling board and try to undo changes and it will undo my what-if changes done via BPM? Will give it a shot!

@NateS If you scheduled with WhatIfScheduling Method I would assume you could also use the UndoChanges Method to remove the WI.

1 Like

This is a good idea. But I am not sure about the parameters that get passed in. When I traced it, the only parameter is a string value, and it looks to be blank. This is the code I am using to move items in the whatif schedule:

//go through each checked row of data
//run what-if scheduling to move the job to the sales ship by

var job = (from ttResults_row in queryResultDataset.Results where  ttResults_row.Calculated_CheckBox == true select ttResults_row).FirstOrDefault();

string outError;
bool ScheduleFinished = false;
     
     this.CallService<Erp.Contracts.ScheduleEngineSvcContract>(bo => {bo.GetScheduleRecord(ref ScheduleEngineTableSet);}); 
               
        //Set schedule engine parameters in the Schedule Table Set
        if (job != null)
          {
          
            var schedEngRow = ScheduleEngineTableSet.ScheduleEngine.NewRow();
            
            schedEngRow["Company"] = callContextClient.CurrentCompany;
            schedEngRow["JobNum"] = job.JobProd_JobNum;
            schedEngRow["AssemblySeq"] = 0;
            schedEngRow["OprSeq"] = 0;
            schedEngRow["OpDtlSeq"] = 0;
            schedEngRow["StartDate"] = DateTime.Today;
            schedEngRow["StartTime"] = 0;
            schedEngRow["EndDate"] = job.JobHead_Date08;
            schedEngRow["EndTime"] = 0;
            schedEngRow["WhatIf"] = true;
            schedEngRow["Finite"] = true ;
            schedEngRow["SchedTypeCode"] = "JJ";
            schedEngRow["SetupComplete"] = false;
            schedEngRow["ProductionComplete"] = false;
            schedEngRow["OverrideMtlCon"] = false;
            schedEngRow["OverRideHistDateSetting"] = 2;
            schedEngRow["RecalcExpProdYld"] = false;
            schedEngRow["UseSchedulingMultiJob"] = true;
            schedEngRow["SchedulingMultiJobIgnoreLocks"] = false;
            schedEngRow["SchedulingMultiJobMinimizeWIP"] = false;
            schedEngRow["SchedulingMultiJobMoveJobsAcrossPlants"] = false;
            schedEngRow["RowMod"] = "A";
            schedEngRow["ScheduleDirection"] = "End";
            
            ScheduleEngineTableSet.ScheduleEngine.Add(schedEngRow);
            job.Calculated_CheckBox = false;
            
        }
    
    
    //Schedule the jobs
    this.CallService<Erp.Contracts.ScheduleEngineSvcContract>(
                    bo => {bo.MoveJobItem(ScheduleEngineTableSet, out ScheduleFinished, out outError); });  
    

This code gets looped for every job we have checked off in the UBAQ. This does work well for getting the what-if schedule in the right place. I just can’t find an easy way to undo it all at once.

Do you know anything about the parameters that get passed into Erp.ScheduleEngine.UndoChanges?

Here is my trace:

<tracePacket>
  <businessObject>Erp.Proxy.BO.ScheduleEngineImpl</businessObject>
  <methodName>UndoChanges</methodName>
  <appServerUri>https://centralusdtpilot01.epicorsaas.com/SaaS512Pilot/</appServerUri>
  <returnType>System.Void</returnType>
  <localTime>12/1/2022 11:25:59:9293248 AM</localTime>
  <threadID>1</threadID>
  <correlationId>5d655b74-ab1e-4b7d-b9dd-d425e7d46880</correlationId>
  <executionTime total="2788" roundTrip="2787" channel="0" bpm="0" bpmDataForm="0" other="1" />
  <retries>0</retries>
  <parameters>
    <parameter name="JobsMoved" type="System.String"><![CDATA[]]></parameter>
    <parameter name="ds" type="Erp.BO.ScheduleEngineDataSet">
      <ScheduleEngineDataSet xmlns="http://www.epicor.com/Ice/300/BO/ScheduleEngine/ScheduleEngine">
        <ScheduleEngine>
          <Company>VTAERO</Company>
          <JobNum>32384/1</JobNum>
          <AssemblySeq>0</AssemblySeq>
          <OprSeq>0</OprSeq>
          <OpDtlSeq>0</OpDtlSeq>
          <WhatIf>false</WhatIf>
          <Finite>false</Finite>
          <SetupComplete>false</SetupComplete>
          <ProductionComplete>false</ProductionComplete>
          <ConsiderPriority>false</ConsiderPriority>
          <OverrideMtlCon>false</OverrideMtlCon>
          <RecalcExpProdYld>false</RecalcExpProdYld>
          <UpdateJobOpDtl>false</UpdateJobOpDtl>
          <JobEngineered>false</JobEngineered>
          <UseSchedulingMultiJob>false</UseSchedulingMultiJob>
          <SchedulingMultiJobIgnoreLocks>false</SchedulingMultiJobIgnoreLocks>
          <SchedulingMultiJobMinimizeWIP>false</SchedulingMultiJobMinimizeWIP>
          <SchedulingMultiJobMoveJobsAcrossPlants>false</SchedulingMultiJobMoveJobsAcrossPlants>
          <SysRowID>b78d37ff-062c-4191-8ffd-b99197ac7e56</SysRowID>
          <RowMod>A</RowMod>
        </ScheduleEngine>
      </ScheduleEngineDataSet>
    </parameter>
  </parameters>
  <paramDataSetChanges>
    <paramDataSet name="ds" useDataSetNbr="0">
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="Company"><![CDATA[VTAERO]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="JobNum"><![CDATA[32384/1]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="AssemblySeq"><![CDATA[0]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="OprSeq"><![CDATA[0]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="OpDtlSeq"><![CDATA[0]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="WhatIf"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="Finite"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="SetupComplete"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="ProductionComplete"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="ConsiderPriority"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="OverrideMtlCon"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="RecalcExpProdYld"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="UpdateJobOpDtl"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="JobEngineered"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="UseSchedulingMultiJob"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="SchedulingMultiJobIgnoreLocks"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="SchedulingMultiJobMinimizeWIP"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="SchedulingMultiJobMoveJobsAcrossPlants"><![CDATA[False]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="SysRowID"><![CDATA[b78d37ff-062c-4191-8ffd-b99197ac7e56]]></changedValue>
      <changedValue tableName="ScheduleEngine" rowState="Added" rowNum="0" colName="RowMod"><![CDATA[A]]></changedValue>
    </paramDataSet>
  </paramDataSetChanges>
</tracePacket>

I also see this method that fires after the undo: Erp.Proxy.BO.SchedulingBoardImpl.BuildJobLine() Is that related?
Thank you for your time!

I don’t follow what you are trying to do. If you want a new what if you have to run global again anyways

Just run Undo All Changes from the scheduling board you are using. Why are you trying to customize?

I am creating a dashboard from a UBAQ. The UBAQ shows all the order releases that are due to ship within the parameter window. From this list of releases, we look at the related jobs. Using the two we can determine the open value. This is the value of all open releases within the window. (Remaining Qty * Unit Price)

Now that I know what the value is for each job, I can see which jobs I would like to potentially move in the schedule. Say we see a lot of load out in March, but we don’t have any load right now. We flag those jobs, and instead of waiting until March to run them, we schedule them to run now.

The UBAQ allows the user to check the job, and reschedule it using what-if scheduling to a new date.

That all works as intended. The BPM inside the UBAQ is creating WI records, and they appear to be accurate.

Now, I have all these jobs moved around in the WI schedule. We only move those around so that we could run the schedule impact report and review the ResourceTimeUsed table. Using this information, we might decide the WI schedule is perfect, and then we want to apply it. Or we might decide that the WI schedule is not good enough, and we want to start again to pull forward a new set of jobs.

In both cases we will need to either undo all the changes, or approve all the changes. None of the changes were made in a scheduling board, they were all made via BPM. So I have to figure out how to undo and approve changes made from a BPM.

I think that I need the UndoChanges (and probably ApproveChanges) method to reference my ScheduleEngineTableSet that gets created in the custom code bit I posted earlier. But those things happen on either side of a condition statement that checks for the actionID.

I have attached the unwieldy thing here if you’re brave enough.
OpenValueJobs-ForWhatIf.baq (155.4 KB)

Since I need to reference the ScheduleEngineTableSet for UndoChanges, I thought I should basically recreate the custom code element I used to create the WI record.
I believe this should look at the job number of the checked row, then pass that job number to the UndoChanges method. But I keep getting an error when I pass anything into that string parameter:

//go through each checked row of data
//run what-if scheduling to move the job to the sales ship by

var job = (from ttResults_row in queryResultDataset.Results where  ttResults_row.Calculated_CheckBox == true select ttResults_row).FirstOrDefault();
  var movedJob = "";
     ScheduleEngineTableSet = new Erp.Tablesets.ScheduleEngineTableset();     
     this.CallService<Erp.Contracts.ScheduleEngineSvcContract>(bo => {bo.GetScheduleRecord(ref ScheduleEngineTableSet);}); 
                    

        //Set schedule engine parameters in the Schedule Table Set
        if (job != null)
          {            
          movedJob = job.JobProd_JobNum.ToString();
          job.Calculated_CheckBox = false; 
          }
    
    //Undo the changes
    this.CallService<Erp.Contracts.ScheduleEngineSvcContract>(
                    bo => {bo.UndoChanges(movedJob,ScheduleEngineTableSet); });  
    

The error is:


## System Information ##
==================

AppServer Connection: https://centralusdtpilot01.epicorsaas.com/SaaS512Pilot
Form Name: Business Activity Query Designer
Customization Name: 
Menu ID: XABA3040
Software Version: 4.2.200.0

============

Server Side Exception

BPM runtime caught an unexpected exception of 'ArgumentOutOfRangeException' type.
See more info in the Inner Exception section of Exception Details.

Exception caught in: Epicor.ServiceModel

## Error Detail ##
============

##!Correlation ID:##!  2c2c49c9-b676-445d-82c5-0001aacbcac9
##!Description:##!  BPM runtime caught an unexpected exception of 'ArgumentOutOfRangeException' type.
See more info in the Inner Exception section of Exception Details.
##!Program:##!  Epicor.ServiceModel.dll
##!Method:##!  Entry
##!Line Number:##!  125
##!Column Number:##!  13
##!Original Exception Type:##!  ArgumentOutOfRangeException
##!Server Trace Stack:##!     at Epicor.Utilities.StringExtensions.Entry(String sourceString, Int32 index, Char delimiter) in C:\_releases\ICE\ICE4.2.200.5\Source\Shared\Framework\Epicor.ServiceModel\Utilities\StringExtensions.cs:line 125
   at Erp.Services.BO.ScheduleEngineSvc.UndoChanges(String JobsMoved, ScheduleEngineTableset ds) in C:\_releases\ERP\ERP11.2.200.0\Source\Server\Services\BO\ScheduleEngine\ScheduleEngine.cs:line 2630
   at Erp.Services.BO.ScheduleEngineSvcFacade.UndoChanges(String JobsMoved, ScheduleEngineTableset ds) in C:\_releases\ERP\ERP11.2.200.0\Source\Server\Services\BO\ScheduleEngine\ScheduleEngineSvcFacade.cs:line 727
   at Epicor.Customization.Bpm.Ubaq.RunCustomActionBaseDirective_WISchedule_DC5597FB62F4458AAA690D3C5318BD4C.<>c__DisplayClass20_0.<A005_CustomCodeAction>b__2(ScheduleEngineSvcContract bo)
   at Epicor.Customization.Bpm.DirectiveBase`2.CallService[TService](Action`1 action) in C:\_releases\ICE\ICE4.2.200.5\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.Plugins.cs:line 362
   at Epicor.Customization.Bpm.Ubaq.RunCustomActionBaseDirective_WISchedule_DC5597FB62F4458AAA690D3C5318BD4C.A005_CustomCodeAction()
   at Epicor.Customization.Bpm.Ubaq.RunCustomActionBaseDirective_WISchedule_DC5597FB62F4458AAA690D3C5318BD4C.ExecuteCore(Int32 step)
   at Epicor.Customization.Bpm.DirectiveBase`2.Execute() in C:\_releases\ICE\ICE4.2.200.5\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 330
   at Epicor.Customization.Bpm.DirectiveBase`2.Execute(TParam parameters) in C:\_releases\ICE\ICE4.2.200.5\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 222



## Client Stack Trace ##
==================
   at Ice.Cloud.ProxyBase`1.CallWithCommunicationFailureRetry(String methodName, ProxyValuesIn valuesIn, ProxyValuesOut valuesOut, RestRpcValueSerializer serializer)
   at Ice.Cloud.ProxyBase`1.CallWithMultistepBpmHandling(String methodName, ProxyValuesIn valuesIn, ProxyValuesOut valuesOut, Boolean useSparseCopy)
   at Ice.Cloud.ProxyBase`1.Call(String methodName, ProxyValuesIn valuesIn, ProxyValuesOut valuesOut, Boolean useSparseCopy)
   at Ice.Proxy.BO.DynamicQueryImpl.RunCustomAction(DynamicQueryDataSet queryDS, String actionID, DataSet queryResultDataset)
   at Ice.Adapters.DynamicQueryAdapter.<>c__DisplayClass43_0.<RunCustomAction>b__0(DataSet datasetToSend)
   at Ice.Adapters.DynamicQueryAdapter.ProcessUbaqMethod(String methodName, DataSet updatedDS, Func`2 methodExecutor, Boolean refreshQueryResultsDataset)
   at Ice.Adapters.DynamicQueryAdapter.RunCustomAction(DynamicQueryDataSet queryDS, String actionId, DataSet updatedDS, Boolean refreshQueryResultsDataset)
   at Ice.UI.App.BAQDesignerEntry.BAQTransaction.<>c__DisplayClass379_0.<CallRunCustom>b__0(Int32& rowReturned)
   at Ice.UI.App.BAQDesignerEntry.Forms.BAQDiagramForm.ShowQueryResults(DataSet dsResults, getQueryResult getResults, ReportAdditionalInfo additionalInfo)
   at Ice.UI.App.BAQDesignerEntry.BAQTransaction.CallRunCustom()

## Inner Exception ##
===============
Specified argument was out of the range of valid values. (Parameter 'Position is greater than number of delimited values')

##  ##


I don’t see a way to get records from the ScheduleEngine. Otherwise, I would get the record I want to undo, then undo it.

I also need to be able to accept the changes to the what if schedule (thus applying them to the actual schedule). Is there a method for that?

I am considering skipping all this custom code stuff, since it doesn’t seem to be working, and make my managers do the whole global scheduling to remove what if values.

To that end - if I make a bunch of what-if changes (outside of a scheduling board) how do I apply those changes to the schedule? Can global scheduling also take the what-if and move it into the actual?
Thanks!

@NateS You are very close to making undo work. I had not used it before, but I have a baq that schedules, so I made a WI schedule, so I could make an UndoChanges. Use the same scheduleenginetableset structure you do for scheduling the job. Look at your trace and then remove ones that are missing like startdate, starttime down to WhatIf and SchedTypeCode, ScheduleDirection. You are also going to add a few like UpdateJobOpDtl, and ConsiderPriority I copied them and the values out of the trace and just fixed them in the code.

Then call UndoChanges(“”,Ds) with an empty string and your dataset and it will undo the WI.

1 Like

Thank you for the vote of confidence! I need that right now, as this project is pushing my abilities.

2 Likes

Yes! Yes! Yes! This works great!!! Thank you so much for the nudge! I am going to clean up a few details and then I will post back a working copy.

2 Likes

Here it is! I am sure I will make some adjustments as this gets used, but this is my outline. If you happen to load it on your test server, let me know if you see anything wrong with it! There are a lot of moving parts here, but this is my thinking:

Import RTU Dashboard and BAQ:
RTU.dbd (194.5 KB)
This shows the time used per resource group. It shows both the What-if time and the actual time. You probably have to tweak the BAQ to use your resource groups. I only have three that I care about for now. So they are hard-coded in the BAQ. If you don’t have any what-if changes, then the totals for what-if true and what-if false should be equal. If you do have what if changes, you can see the difference in time here. This is an easy way to validate that the what-if schedule has been changed, and by how much.

Next import the OpenValue Dashboard:
OpenValueForWhatIf.dbd (469.4 KB)
This should also pull in a UBAQ and BPM. You will also need to apply this customization to your Open Value Dashboard:
App.OpenValueRTUSales.MainController_Customization_custom1_CustomExport.xml (34.2 KB)

Now this dashboard has three custom actions. Update WI Schedule, Undo Changes, and Accept Changes. When the dashboard runs it asks the user for a date range to return order releases from (using OrderRel.ReqDate). It also asks the user for a new sales ship by date. I am storing this in JobHead_Date08. This is the shipping goal that sales wants to target.

The process is to review the list of jobs, click the check box for each job you want to move the shipping date for. The shipping date will be updated to the sales ship by date. So you check off some jobs, and watch the open value total increase. This shows you how much value the open releases are worth for the jobs you selected.

After you have a list of jobs selected, you have to go to Actions and Update WI Schedule. This updates the RTU table to include the new what-if dates you requested. Now you can refresh the RTU dashboard and see the new difference in actual vs. what-if time used. You can also review the changes with Schedule Impact Report.

If you like the changes, go back to the Open Value Dashboard and check off those jobs you just changed the WI schedule for. Now go to Actions > Accept Changes. You can then run RTU dash again to see the WI and actual times are the same again. But you will need to run global scheduling to get the real load information (right?)

If you don’t like the changes to the WI schedule, instead of clicking Accept Changes after you check the jobs, click Undo Changes. After that you can review the RTU dashboard again to see the changes have been removed and the times are equal again.

Thank you for all your help getting this going! I think this is going to work. Please let me know if you see any deal-breakers, show-stoppers, or nefarious bugs. I’ll post back if I make any major updates.

EDIT: I already found an issue. The BPM locks jobs when it accepts changes. If any job is locked, this process won’t touch them. So, I will have to ask the user if they want to unlock any locked jobs, or maybe just tell them we will unlock the jobs and relock them at the end.

EDIT: Posted back to fix the locked job issue:
OpenValueForWhatIf.dbd (466.1 KB)

3 Likes

Nate, nice work! Great perseverance!

1 Like

I am still not sure about this part. When I (run the apply changes method) apply changes, what is really happening? I know the req by date for those jobs are getting updated. But, are other jobs getting moved? I ran Shop Load reports before and after my accepted changes, and I don’t see any difference in load.

The first time I ran the shop load report was after I accepted changes, applying the WI schedule to the actual schedule. The second time I ran it was after running the global rescheduling process. I expected to see a difference in load, or at least load distribution. but the before/after reports were identical. Maybe I don’t need to be doing global reschedule after applying changes?

@NateS In my serverlog there were a ton of writes to ResourceTimeUsed and ShopLoad when the custom action ran, so I would assume that accept changes ran all of the updates needed.

You may want to update the solution to your UBAQ.