Process guide framework (WMS)

So existing WMS design is simple but lack the role separation among classes and things can get pretty complex when number of scenarios grow , 

The process guide framework divides the execution flow into individual components. Each component has well-defined responsibilities and extension points.

The following are the key components in the redesigned process:

  • ProcessGuideController – This class orchestrates the overall execution of the business process. It defines the factories that instantiate the step and the navigation agent. These subsequently constitute the process execution. The class also contains the clean-up logic for cancellation or exiting the process.
  • ProcessGuideStep – This class represents one single step in the business process. This class defines the factories. These factories instantiate a page builder, actions, and data processors. The class is responsible for invoking them in the correct sequence.
  • ProcessGuideNavigationAgent – This class is responsible for navigation between the steps. When a step is completed, the navigation agent defines the next step. It passes any parameters that the previous step may need to communicate to the next one.
  • ProcessGuidePageBuilder – This class is responsible for instantiating the user interface.
  • ProcessGuideAction – This class represents an action, shown as a button to the user.
  • ProcessGuideDataProcessor – This class is responsible for processing the user entered data in a field.

Below is a real use case example. It omits all the business logic classes. The goal is to explain the design and process of the framework.

This is custom purchase order receiving process , developed to cater peculiar client needs , there is much around it which is not in the scope

Here are the classes to cater this process , nature of these classes have been explained above.

[WHSWorkExecuteMode(WHSWorkExecuteMode::AXPPOReceiving)] 
public final class AXPPurchProcessGuideProductReceiptController extends ProcessGuideController 
{ 
    #AXPAWMS 
    protected final ProcessGuideStepName initialStepName() 
    { 
        return classStr(AXPPurchProcessGuidePromptPurchIdStep); 
    } 
 
    protected ProcessGuideNavigationRoute initializeNavigationRoute() 
    { 
        ProcessGuideNavigationRoute navigationRoute = new ProcessGuideNavigationRoute(); 
        navigationRoute.addFollowingStep(classStr(AXPPurchProcessGuidePromptPurchIdStep), classStr(AXPPurchProcessGuidePromptPurchLineListStep)); 
        if(this.parmClickedAction() == #ActionProcessReceipt) 
        { 
            navigationRoute.addFollowingStep(classStr(AXPPurchProcessGuidePromptPurchLineListStep), classStr(AXPPurchProcessGuidePromptPurchLineListStep)); 
        } 
        else 
        { 
            navigationRoute.addFollowingStep(classStr(AXPPurchProcessGuidePromptPurchLineListStep), classStr(AXPPurchProcessGuidePromptPurchLineQtyStep)); 
        } 
        navigationRoute.addFollowingStep(classStr(AXPPurchProcessGuidePromptPurchLineQtyStep), classStr(AXPPurchProcessGuidePromptPurchLineListStep)); 
        return navigationRoute; 
         
    } 
}

The controller class holds the navigation map of the process. It defines where the processes should start and what step should be executed next. As seen in the code snippet, we can intuitively apply conditional navigation as well.

Steps classes then holds the actual process logic and any validations , it also calls the pagebuilder which builds the interface for user 

[ProcessGuideStepName(classStr(AXPPurchProcessGuideProductReceiptPrintStep))] 
public class AXPPurchProcessGuideProductReceiptPrintStep extends ProcessGuideStep 
{ 
    #WHSRF 
    #ProcessGuideActionNames 
 
    PackingSlipId   packingSlipId; 
    PurchId         purchId; 
 
    protected final ProcessGuidePageBuilderName pageBuilderName() 
    { 
        return classStr(AXPPurchProcessGuideProductReceiptPrintBuilder); 
    } 
 
    protected void validateControls() 
    { 
        packingSlipId = processingResult.fieldValues.lookupStr(ProcessGuideDataTypeNames::PackingSlip); 
        purchId = processingResult.fieldValues.lookupStr(#PurchId); 
 
        if (!PurchTable::exist(purchId) && purchId != "") 
        { 
            throw error(strFmt('Purchase order %1 does not exist', purchId)); 
        } 
        if (packingSlipId == "") 
        { 
            throw error(strFmt('Packing slip number must be specified')); 
        } 
 
        super(); 
    } 
 
    protected void doExecute() 
    { 
        WhsrfPassthrough  pass = controller.parmSessionState().parmPass(); 
        super(); 
 
// business logic here 
// business logic here 
    } 
 
} 

WhsrfPassthrough this class holds the session data throughout. could be used to set and get the records through out the process. This helps when you traverse through different screens.

[ProcessGuidePageBuilderName(classStr(AXPPurchProcessGuidePromptPurchIdPageBuilder))] 
public class AXPPurchProcessGuidePromptPurchIdPageBuilder extends ProcessGuidePageBuilder 
{ 
    #WHSRF 
    protected final void addDataControls(ProcessGuidePage _page) 
    { 
        _page.addTextBox(#PurchId, "@SYS114676", extendedTypeNum(PurchId)); 
    } 
 
    protected final void addActionControls(ProcessGuidePage _page) 
    { 
        #ProcessGuideActionNames 
        _page.addButton(step.createAction(#ActionOK), true); 
        _page.addButton(step.createAction(#ActionCancelExitProcess)); 
    } 
 
} 
Figure 1: User Inputs the PO 
Figure 2: PO Lines are retrieved, user selects the line. 
Figure 3: User Inputs the Quantity Received 

Leave a Reply