Custom List Page in D365 FnO Warehouse Mobile App via Process Guide Framework

Custom List Page in D365 FnO Warehouse Mobile App via Process Guide Framework

Introduction

Creating a custom list page in the Dynamics 365 Finance and Operations (D365 FnO) Warehouse Mobile App allows users to interact with data intuitively and efficiently. Leveraging the Process Guide Framework, we can build scalable solutions with modular components that streamline user interactions and simplify navigation. This article will cover how to develop such a page for replenishment requests, detailing each core component involved.

Core Components

The architecture of a custom list page revolves around the following five key components:

1. Controller: Manages the workflow initiation and navigation.

2. Navigation Factory: Defines how users navigate between steps.

3. Steps: Encapsulate the business logic for each stage of the process.

4. Page Builder: Constructs the UI elements that display data to users.

5. Decorator Factory: Customizes data rendering rules in the Warehouse Mobile App.


1. Controller

The Controller is the starting point of the process. It defines the initial step of the workflow and links to the navigation factory, which determines how the process moves between steps.

x++

[WHSWorkExecuteMode(WHSWorkExecuteMode::ReplenishmentRequest)]

public class WHSProcessGuideReplenishmentRequestController extends ProcessGuideController

{

protected final ProcessGuideStepName initialStepName()

{

return classStr(WHSProcessGuideReplenishmentRequestListStep);

}

protected ProcessGuideNavigationAgentAbstractFactory navigationAgentFactory()

{

return new WHSProcessGuideReplenishmentRequestNavigationFactory();

}

}

?

In this example:

- The initial step is set to WHSProcessGuideReplenishmentRequestListStep.

- The navigation factory is used to guide transitions between steps.


2. Navigation Factory

The Navigation Factory controls the flow between different steps in the process. Depending on the current step, the appropriate navigation agent is initialized to handle the next action.

x++

public class WHSProcessGuideReplenishmentRequestNavigationFactory extends ProcessGuideNavigationAgentAbstractFactory

{

public final ProcessGuideNavigationAgent createNavigationAgent(ProcessGuideINavigationAgentCreationParameters _parameters)

{

ProcessGuideNavigationAgentCreationParameters creationParameters = _parameters as ProcessGuideNavigationAgentCreationParameters;

if (!creationParameters)

{

throw error(Error::wrongUseOfFunction(funcName()));

}

WhsrfPassthrough pass = creationParameters.controller.parmSessionState().parmPass();

return this.initializeNavigationAgent(creationParameters.stepName, pass);

}

private ProcessGuideNavigationAgent initializeNavigationAgent(

ProcessGuideStepName _currentStep,

WhsrfPassthrough _pass)

{

switch (_currentStep)

{

case classstr(WHSProcessGuideReplenishmentRequestListStep):

return new WHSProcessGuideReplenishmentRequestDetailNavigationAgent();

case classstr(WHSProcessGuideReplenishmentRequestDetailStep):

return new WHSProcessGuideReplenishmentRequestListNavigationAgent();

default:

return new WHSProcessGuideReplenishmentRequestListNavigationAgent();

}

}

}

?

Here, the factory initializes the navigation agent based on the current step, enabling a smooth transition between screens.


3. Steps

Each Step defines the logic for what happens during that phase of the process. For instance, the replenishment request list step ensures that valid input is provided and data is passed between steps.

x++

[ProcessGuideStepName(classStr(WHSProcessGuideReplenishmentRequestListStep))]

public class WHSProcessGuideReplenishmentRequestListStep extends ProcessGuideStep

{

protected final ProcessGuidePageBuilderName pageBuilderName()

{

return classStr(WHSProcessGuideReplenishmentRequestListPageBuilder);

}

protected boolean isComplete()

{

WhsrfPassthrough pass = controller.parmSessionState().parmPass();

SalesId salesId = pass.lookup(ProcessGuideDataTypeNames::RepReqRecId);

return true;

}

protected void doExecute()

{

str _repReqRecId = controller.parmClickedData();

if (_repReqRecId)

{

WhsrfPassthrough pass = controller.parmSessionState().parmPass();

pass.insert(ProcessGuideDataTypeNames::RepReqRecId, _repReqRecId);

}

super();

}

}

?

This step handles the execution of user input and checks whether the required data is available to proceed.


4. Page Builder

The Page Builder is responsible for creating the layout and structure of the list page. It adds controls such as labels and action buttons to display data and interact with it.

x++

[ProcessGuidePageBuilderName(classStr(WHSProcessGuideReplenishmentRequestListPageBuilder))]

public class WHSProcessGuideReplenishmentRequestListPageBuilder extends ProcessGuidePageBuilder

{

private const str KeyValuePairWithSeparator = '%1~:::~%2';

private const str NewLineCharacter = '\n';

private boolean queryHasUnspecifiedFilters = false;

protected void addControls(ProcessGuidePage _page)

{

super(_page);

WhsrfPassthrough pass = controller.parmSessionState().parmPass();

WHSRFMenuItemTable menuItem = WHSRFMenuItemTable::find(pass.parmMenuItem());

WHSReplenishmentRequests replenishmentReq;

Query query = new Query();

QueryBuildDataSource dsInventTable = query.addDataSource(tableNum(WHSReplenishmentRequests));

// Apply filters before executing the query

TableId queryTableId = tableName2Id("WHSReplenishmentRequests");

var filterValuesSubHeader = this.UpdateQueryFilterValues(query, queryTableId, menuItem, pass);

_page.addLabel(InquiryFiltersControlName, filterValuesSubHeader, extendedTypeNum(WHSRFUndefinedDataType));

// Execute the query and display records

while select firstonly10 * from replenishmentReq where replenishmentReq.CompletionDateTime == DateTimeUtil::minValue()

{

this.buildTableContents(_page, replenishmentReq);

}

this.buildPagingControls(_page);

}

private str UpdateQueryFilterValues(Query query, TableId queryTableId, WHSRFMenuItemTable menuItem, WhsrfPassthrough pass)

{

str filtersSubHeader = '';

QueryBuildDataSource qbds = query.dataSourceTable(queryTableId);

for (int i = 1; i <= _query.queryFilterCount(qbds); i++)

{

QueryFilter filter = _query.queryFilter(i, qbds);

if (!filter.value())

{

queryHasUnspecifiedFilters = true;

SysDictField dictField = SysDictField::newName(_menuItem.DataInquiryQueryTableName, filter.field());

var newFilterValue = _pass.lookupStr(this.filterFieldControlName(dictField));

if (newFilterValue)

{

filter.value(newFilterValue);

filtersSubHeader += strFmt("@WAX3192", dictField.label(), newFilterValue);

filtersSubHeader += NewLineCharacter;

}

}

}

return filtersSubHeader;

}

private str filterFieldControlName(SysDictField _dictField)

{

return "DetourQueryFilter_" + _dictField.name ();

}

private void buildTableContents(ProcessGuidePage page, WHSReplenishmentRequests replenishmentReq)

{

str cardInformation = '';

str id = int642Str(_replenishmentReq.RecId);

str itemNumber = _replenishmentReq.ItemNumber;

str requestingWorkCell = _replenishmentReq.RequestingWorkCell;

str productName = _replenishmentReq.ProductName;

str requestedDateTime = datetime2Str(_replenishmentReq.RequestedDateTime);

str priority = int2Str(_replenishmentReq.Priority);

cardInformation += strFmt(KeyValuePairWithSeparator, "itemNumber", itemNumber);

cardInformation += NewLineCharacter;

cardInformation += strFmt(KeyValuePairWithSeparator, "productName", productName);

cardInformation += NewLineCharacter;

cardInformation += strFmt(KeyValuePairWithSeparator, "requestingWorkCell", requestingWorkCell);

cardInformation += NewLineCharacter;

_page.addLabel(id, cardInformation, extendedTypeNum(NotesLine));

}

private void buildPagingControls(ProcessGuidePage _page)

{

_page.addButton(step.createAction(ActionCancelExitProcess));

_page.addButton(step.createAction(ActionBackProcess));

}

}

?

The Page Builder also supports dynamic filtering through the UpdateQueryFilterValues method, which applies filters based on user inputs or predefined rules.

---

5. Decorator Factory

The Decorator Factory handles the custom rules for rendering the data on the mobile app. It ensures the display aligns with the process requirements, such as navigating between inquiries.

x++

[WHSWorkExecuteMode(WHSWorkExecuteMode::ReplenishmentRequest)]

public class WHSMobileAppServiceXMLDecoratorFactoryReplenishment implements WHSIMobileAppServiceXMLDecoratorFactory

{

public WHSMobileAppServiceXMLDecorator getDecorator(container _con)

{

return new WHSMobileAppServiceXMLDecoratorReplenishment();

}

}

public class WHSMobileAppServiceXMLDecoratorReplenishment extends WHSMobileAppServiceXMLDecoratorInquiry

{

protected void registerRules()

{

rulesList.addEnd(WH

SMobileAppServiceDecoratorRuleGenericDataInquiryDisplayArea::construct());

}

public WHSMobileAppPagePattern requestedPattern()

{

return WHSMobileAppPagePattern::InquiryWithNavigation;

}

}

?

---

Conclusion

Using the Process Guide Framework to create a custom list page in the D365 FnO Warehouse Mobile App offers a modular, maintainable approach to enhancing user experiences. By implementing core components like the Controller, Navigation Factory, Steps, Page Builder, and Decorator Factory, developers can easily build sophisticated, business-specific applications. This approach also simplifies maintenance, ensuring a clear separation of concerns and flexible customization.

By removing unnecessary code and focusing on essential functionality, this framework allows for the creation of high-performance, user-friendly mobile apps.



?

Clara Safwat

Senior Microsoft Dynamics 365 FO & CRM developer | Technical Consultant

1 周

Hi Muhammad Ghous Sarwar tried the same but didn’t work for me, is there required setup before starting develop?

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了