Category Archives: Form Styles, Customisation & Patterns

Form Styles & Patters

D365 FO/AX7: Mobile App & Mobile Workspaces With Example

Introduction – The Microsoft Dynamics 365 mobile app allows you to access your business processes, data and records from mobile phones and tablets. Microsoft Dynamics D365 Mobile workspaces can be enabled in a very less time with almost no effort. Using Microsoft Dynamics D365 fins and operations mobile user can easily perform below activities :

1. User can view Microsoft Dynamics D365 finance and operations business data in mobile app.

2. User can edit or delete Microsoft Dynamics D365 finance and operations business data in mobile app.

3. Admin can add certain tasks and CRUD operations in D365 workspace and mobile app. User can perform those operations in D365 mobile app.

Example – Below are the steps to create your first Microsoft Dynamics D365 mobile app.

  1. In this scenario we are using vendor master and we will public vendor master workspace and user will be able to view vendor master in D365 mobile app.
  1. Go to settings menu and click on mobile app in Microsoft Dynamics 365.
  1. Create new Microsoft Dynamics 365 mobile app.
  1. Fill all the details of Microsoft Dynamics 365 mobile app.
  1. Add page in Microsoft Dynamics 365 mobile app.
  1. Select fields
  1. Save the Microsoft Dynamics 365 mobile app.
  2. Publish the Microsoft Dynamics 365 workspace and mobile app.
  3. Login into your Microsoft Dynamics 365 mobile app and start using it

D365/AX7: Method to call when record is marked – markChanged() FormDataSource

Requirement:

The requirement was to display information of selected records of form Grid on a string control.The information on custom string control should change during the marking of records in real time.

Sample Code:

Copy the method in form datasource.

  1. public void markChanged()
  2.         {
  3.             AcxBrandMaster  acxBrandMaster1;
  4.             str brandStr;
  5.             MultiSelectionHelper    multiSelectionHelper =  MultiSelectionHelper::construct();
  6.             multiSelectionHelper.parmDatasource(AcxBrandMaster_DS);
  7.             acxBrandMaster1 = multiSelectionHelper.getFirst();
  8.             FormStringControl1.text(“”);
  9.             while (acxBrandMaster1.RecId)
  10.             {
  11.                 brandStr = brandStr +  acxBrandMaster1.Brand;
  12.                 acxBrandMaster1 = multiSelectionHelper.getNext();
  13.             }
  14.             FormStringControl1.text(brandStr) ;
  15.             super();
  16.         }

D365/AX7:Create A Tree Node Form Using Pattern – Simple List & Details-Tree

Requirement

Create A New Tree Node Form Using Pattern – Simple List & Details-Tree.

Untitled

Steps

  1. Create a new table AcxNodeTable which contains fields -Node(str 10) & NodeDescription(str 60).
  2. Create a new table AcxNodeReferenceTable which contains fields – Node (int64) & ParentNode (int64).
  3. Create a new form AcxNode using pattern – *Simple List & Details – Tree*.The form design should look as per below screen.
  4. Create the below methods in form.

[code language = “cpp”]
[Form]
public class AcxNode extends FormRun
{
boolean isNewNode;
boolean isModified;
TreeItemIdx newItemIdx;
TreeItemIdx lastRecordIdx;
int64 lastRecordRecId;
int64 range;
int64 nodeGroupHierarchyParentRecid;
List delRecList;
public void deleteGroupCode(recId _parentrecId)
{
AcxNodeReferenceTable nodeReferenceTableLocal;
AcxNodeReferenceTable nodeReferenceTableLoop;
while select * from nodeReferenceTableLoop
where nodeReferenceTableLoop.ParentNode == _parentrecId</pre>
{
select count(recId) from nodeReferenceTableLocal
where nodeReferenceTableLocal.ParentNode == nodeReferenceTableLoop.RecId;
if(nodeReferenceTableLocal.RecId &gt; 0)
{
delRecList.addEnd(nodeReferenceTableLoop.ParentNode);
element.deleteGroupCode(nodeReferenceTableLoop.RecId);
}
else
{
delRecList.addEnd(nodeReferenceTableLoop.ParentNode);
}
}
}

public boolean deleteNodeGroup(recId _recId)
{
AcxNodeTable nodeTableLocal;
AcxNodeReferenceTable nodeReferenceTable;
DialogButton dialogButton;
ListEnumerator enumer;
boolean allDeleted = true;

if(_recId)
{
select firstonly1 nodeReferenceTable
where nodeReferenceTable.RecId == _recId;
dialogButton = Box::yesNo(“Do you want to delete node and its subnodes?”, DialogButton::No);
if( dialogButton == DialogButton::Yes )
{
try
{
delRecList = new List(Types::Int64);
delRecList.addStart(nodeReferenceTable.Node);
element.deleteGroupCode(_recId);
enumer = delRecList.getEnumerator();
ttsBegin;
while(enumer.moveNext())
{
select forupdate nodeTableLocal
where nodeTableLocal.RecId == enumer.current();
if(nodeTableLocal.validateDelete())
{
nodeTableLocal.delete();
}
else
{
allDeleted = false;
break;
}
}

if(allDeleted)
{
ttsCommit;
return true;
}
else
{
ttsAbort;
return false;
}
}
catch
{
return false;
}
}
else
{
return false;
}
}

else
{
return false;
}
}

public void refreshTree(recId _lastRecid)
{
lastRecordRecId = _lastRecid;
element.loadTree();
WorkplanTreeControl.select(lastRecordIdx);
}

public void addNode(TreeItemIdx _parentIdx)
{
FormTreeItem newItem;
FormTreeItem parentItem;
;

newItem = new FormTreeItem(“”);
newItem.data(0);
newItemIdx = WorkplanTreeControl.addItem(_parentIdx, FormTreeAdd::Sort,newItem);
WorkplanTreeControl.select(newItemIdx);
AcxNodeTable_ds.create();
parentItem = WorkplanTreeControl.getItem(_parentIdx);
nodeGroupHierarchyParentRecid = parentItem.data();
isNewNode = true;
}

/// &lt;summary&gt;
///
/// &lt;/summary&gt;
public void run()
{
super();

element.loadTree();
}

public void loadTree()
{
TreeItemIdx parentItemIdx;
WorkplanTreeControl.deleteAll();
parentItemIdx = this.createRootNode();
this.createChildNodes(parentItemIdx,0);
WorkplanTreeControl.select(WorkplanTreeControl.getRoot());
WorkplanTreeControl.expand(WorkplanTreeControl.getRoot());
}

public TreeItemIdx createRootNode()
{
FormTreeItem parentItem;
parentItem = new FormTreeItem(“Root Of Tree”);
parentItem.data(0);
return WorkplanTreeControl.addItem(FormTreeAdd::Root,FormTreeAdd::First,parentItem);
}

public void createChildNodes(TreeItemIdx _parentIdx, int64 _serviceGroupHierarchyParentRecid)
{
AcxNodeReferenceTable nodeReferenceLocal;
AcxNodeReferenceTable nodeReferenceChild;
AcxNodeTable nodeTable;
FormTreeItem parentItem;
TreeItemIdx newParentIdx;

while select * from nodeReferenceLocal
join * from nodeTable
where nodeReferenceLocal.ParentNode == _serviceGroupHierarchyParentRecid
&amp;&amp; nodeReferenceLocal.Node == nodeTable.RecId
{
select count(recId) from nodeReferenceChild
where nodeReferenceChild.ParentNode == nodeReferenceLocal.RecId;
if(nodeReferenceChild.RecId &gt; 0)
{
parentItem = new FormTreeItem();
parentItem.data(nodeReferenceLocal.RecId);
parentItem.text(nodeTable.NodeDescription);

newParentIdx = WorkplanTreeControl.addItem(_parentIdx,FormTreeAdd::Sort,parentItem);
if(nodeReferenceLocal.RecId == lastRecordRecId)
{
lastRecordIdx = newParentIdx;
}
element.createChildNodes(newParentIdx,nodeReferenceLocal.RecId);
}
else
{
parentItem = new FormTreeItem();
parentItem.data(nodeReferenceLocal.RecId);
parentItem.text(nodeTable.NodeDescription);
newParentIdx = WorkplanTreeControl.addItem(_parentIdx,FormTreeAdd::Sort,parentItem);
if(nodeReferenceLocal.RecId == lastRecordRecId)
{
lastRecordIdx = newParentIdx;
}
}

}
}

[DataSource]
class AcxNodeTable
{
/// &lt;summary&gt;
///
/// &lt;/summary&gt;
public void executeQuery()
{
this.query().dataSourceTable(tableNum(AcxNodeTable)).clearRanges();
this.query().dataSourceTable(tableNum(AcxNodeTable)).clearDynalinks();
this.query().dataSourceTable(tableNum(AcxNodeTable)).addRange(fieldNum(AcxNodeTable, RecId)).value(int642Str(Range));
range = 0;
super();
}

/// &lt;summary&gt;
///
/// &lt;/summary&gt;
public void write()
{
AcxNodeReferenceTable nodeReferenceTable;
super();
if(isNewNode == true)
{
nodeReferenceTable.clear();
nodeReferenceTable.Node = AcxNodeTable.RecId;
nodeReferenceTable.ParentNode = nodeGroupHierarchyParentRecid;
nodeReferenceTable.insert();

newItemIdx = 0;
nodeGroupHierarchyParentRecid = 0;
isNewNode = false;
isModified = false;
element.refreshTree(nodeReferenceTable.RecId);
}
else
{
element.refreshTree(nodeReferenceTable.RecId);
}
isNewNode = false;
}

}

[Control(“CommandButton”)]
class AddNode
{
/// &lt;summary&gt;
///
/// &lt;/summary&gt;
public void clicked()
{
super();
element.addNode(WorkplanTreeControl.getSelection());
}

}

[Control(“CommandButton”)]
class DeleteNode
{
public void clicked()
{
FormTreeItem treeItem;
treeItem = WorkplanTreeControl.getItem(WorkplanTreeControl.getSelection());
if(element.deleteNodeGroup(treeItem.data()))
{
element.loadTree();
}
}

}

[Control(“Tree”)]
class WorkplanTreeControl
{
/// &lt;summary&gt;
///
/// &lt;/summary&gt;
/// &lt;param name = “_OldItem”&gt;&lt;/param&gt;
/// &lt;param name = “_NewItem”&gt;&lt;/param&gt;
/// &lt;param name = “_how”&gt;&lt;/param&gt;
public void selectionChanged(FormTreeItem _OldItem, FormTreeItem _NewItem, FormTreeSelect _how)
{
AcxNodeReferenceTable nodeReferenceTable;
super(_OldItem, _NewItem, _how);
FormTreeItem treeItem;
treeItem = WorkplanTreeControl.getItem(WorkplanTreeControl.getSelection());
select firstonly1 nodeReferenceTable
where nodeReferenceTable.RecId == treeItem.data();
range = nodeReferenceTable.Node;
AcxNodeTable_DS.executeQuery();
AcxNodeTable_DS.research(true);
AcxNodeTable_DS.refresh();
}
}
}
}
[/code]

 

cheers 🙂

piyush adhikari

 

 

D365/AX7:Extend Standard Item lookup in Sales Order, Transfer Order, Inventory Journals etc

Requirement

Some new custom fields are created on extension of table InventTable.Customer wants to see these custom fields in the item lookup of sales order, transfer order & inventory journals etc.

Steps

  1. Create the extension of table InventTable and add your custom fields.
  2. Create the extension of view InventItemIdLookupSimpleView and add your custom fields.
  3. Create the extension of form InventItemIdLookupSimple and add your custom fields in the GRID of the form.
  4. Create the extension of class InventItemIdLookupSimple

[code language = “cpp”]
[ExtensionOf(classstr(InventItemIdLookupSimple))]
final class AcxInventItemIdLookup_Extension
{
protected void addStandardFields()
{
List lookupFields1 = lookupFields;
next addStandardFields() ;
lookupFields1.addEnd((fieldNum(InventItemIdLookupSimpleView, AcxItemCode)));// added my custom field
lookupFields = lookupFields1;
}
}
[/code]

 

 

D365/AX7:OnDisplayOptionInitialize event is retrieving/getting always the first record

Purpose

I have posted sample code of customisation for adding color to a form control of a standard or custom form by using event OnDisplayOptionInitialize.

D365/AX7:Adding color to a form control of a standard or custom form by using event OnDisplayOptionInitialize

But there is issue in above method of customisation i.e OnDisplayOptionInitialize event is retrieving/getting always the first record when you will do conditional coloring.

Sample Code & Solution

This code is working fine on version 8.1 or higher and PU 20 or higher.The code is not working on version 7.3.

Instead of using sender.cursor(),

use eventArgs.record()

Otherwise color will come only on that record where cursor is placed.

[code language=”cpp”]/// /// /// /// /// ///
[FormDataSourceEventHandler(formDataSourceStr(SalesTable, SalesLine), FormDataSourceEventType::DisplayOptionInitialize)]
public static void SalesLine_OnDisplayOptionInitialize(FormDataSource sender, FormDataSourceEventArgs e)
{
SalesLine salesLine //= sender.cursor(); wrong
FormDataSourceDisplayOptionInitializeEventArgs eventArgs = e as FormDataSourceDisplayOptionInitializeEventArgs;
salesLine = eventArgs.record(); //correct
if (salesLine.ItemId == ‘1105’)
{
//eventArgs.displayOption().affectedElementsByControl(sender.formRun().design(0).controlName(“ItemName”).id())
eventArgs.displayOption().textColor(WinAPI::RGB2int(255,0,0));
}
else
{
//eventArgs.displayOption().affectedElementsByControl(sender.formRun().design(0).controlName(“ItemName”).id());
eventArgs.displayOption().textColor(WinAPI::RGB2int(255,255,0));
}
}
[/code]

 

 

 

 

 

D365/AX7:Adding color to a form control of a standard or custom form by using event OnDisplayOptionInitialize

Here is the fixed for conditional coloring.

D365/AX7:OnDisplayOptionInitialize event is retrieving/getting always the first record

Requirement :

Adding color to “Product Name” string control of form “SalesTable” inside line grid.

Untitled

Sample Code & Instructions:

  1. Create a new class.
  2. Copy event handler method OnDisplayOptionInitialize of datasource “SalesLine” in form “SalesTable”.
  3. Paste the event handler method “OnDisplayOptionInitialize in class you created on step 1.[code language=”cpp”]/// ///
    ///
    ///

    ///
    ///
    [FormDataSourceEventHandler(formDataSourceStr(SalesTable, SalesLine), FormDataSourceEventType::DisplayOptionInitialize)]
    public static void SalesLine_OnDisplayOptionInitialize(FormDataSource sender, FormDataSourceEventArgs e)
    {
    FormDataSourceDisplayOptionInitializeEventArgs eventArgs = e as FormDataSourceDisplayOptionInitializeEventArgs;
    eventArgs.displayOption().affectedElementsByControl(sender.formRun().design(0).controlName(“ItemName”).id());
    eventArgs.displayOption().textColor(WinAPI::RGB2int(255,0,0));
    }[/code]

  4. Untitled

 

 

Cheers 🙂

Piyush Adhikari

 

D365/AX7:Ledger/Financial Dimension Lookup In Form (Segmented Control)

Requirement

Ledger/Financial Dimension Lookup In Form (Segmented Control)

Steps

  1. Create 2 fields LedgerDimension (Int64) & AccountType (Enum-LedgerJournalACType) in table.
  2. Create a table relation of field LedgerDimension to DimensionAttributeValueCombination table.

Capture

 

3. Add fields LedgerDimension (Int64) & AccountType (Enum-LedgerJournalACType)             in form.

Capture.JPG

4. Change the properties of segmented control LedgerDimension:

– Auto declaration : Yes
– Account type field : AccountType
– Controller class : DimensionDynamicAccountController
– Filter expression : %1

5.Override the datasource’s field LedgerDimension modified() method

public void modified()
{
super();
SampleTest_LedgerDimension_ds.refresh();
}

6.Override the Form’s segment entry control LedgerDimension lookup() &                               checkUseCustomLookup() methods.

public boolean checkUseCustomLookup(int _accountTypeEnumValue, int _secondaryAccountTypeEnumValue)
{
boolean returnValue;

LedgerJournalACType accountType = any2Enum(_accountTypeEnumValue);
switch (accountType)
{
case LedgerJournalACType::Bank:
case LedgerJournalACType::Cust:
case LedgerJournalACType::FixedAssets:
case LedgerJournalACType::Project:
case LedgerJournalACType::Vend:
returnValue = true;
break;
default:
returnValue = false;
break;
}

return returnValue;
}

public void lookup()
{
switch (SampleTest_LedgerDimension.LedgerJournalACType)
{
case LedgerJournalACType::Bank:
BankAccountTable::lookupBankAccount(this);
break;
case LedgerJournalACType::Cust:
CustTable::lookupCustomer(this);
break;
case LedgerJournalACType::FixedAssets:
AssetTable::lookupAccountNum(this);
break;
case LedgerJournalACType::Vend:
VendTable::lookupVendor(this);
break;
default:
super();
break;
}
}

Capture

Cheers!