Leader Board

Showing posts with label Development. Show all posts
Showing posts with label Development. Show all posts

How to add new action under Context menu in Dynamics AX

Each node in the AOT contains a set of available actions. You can access these actions from the
context menu, which you can open by right-clicking any node.
You can create custom actions for any element in the AOT by enlist a class as a new add-in by following:
1. Create a new menu item and give it a meaningful name, a label, and Help text.
2. Set the menu item’s Object Type property to Class.
3. Set the menu item’s Object property to the name of the class to be invoked by the add-in.
4. Drag the menu item to the SysContextMenu menu.
5. If you want the action available only for certain nodes, you need to modify the verifyItem
method on the SysContextMenu class.

How to Delete Company Transactions in AX 2012 including Trial balance?

To delete Transactions data inside any company in ax, you can do by using the class SysDatabaseTransDelete under AOT.
But you may facing an issue since the Trial Balance will not be deleted so, to delete GL Trans by deleting the following tables:
GeneralJournalAccountEntry
GeneralJournalEntry
LedgerEntryJournal
LedgerEntry
Ledgerjournaltrans
Ledgerjournaltable
To remove the Trial Balance you have to follow the steps below:


1-) Modify 'handleTable()' method by adding 2 new cases
Case TableGroup::TransactionHeader:
Case TableGroup::TransactionLine:
void handleTable(SysDictTable sysDictTable)
{
    TableGroup      tableGroup;

    if (tableSet.in(sysDictTable.id()))
        return;

    tableSet.add(sysDictTable.id()); 

    if (sysDictTable && !sysDictTable.isTmp() && !sysDictTable.isMap())
    {
        tableGroup = sysDictTable.tableGroup();
        // Handle company specific tables to be deleted
        if (sysDictTable.dataPrCompany())
        {
            switch(tableGroup)
            {
                case TableGroup::Transaction:
                case TableGroup::TransactionHeader:
                case TableGroup::TransactionLine:
                case TableGroup::WorksheetHeader:
                case TableGroup::WorksheetLine:
                    this.handleTransTable(sysDictTable);
                    break;
                default:
                    this.handleNonTransTable(sysDictTable);
                    break;
            }
        }
        else
        {
            // Handle global tables to be deleted
            switch(tableGroup)
            {
                case TableGroup::Transaction:
                case TableGroup::TransactionHeader:
                case TableGroup::TransactionLine:
                case TableGroup::TransactionHeader :
                case TableGroup::WorksheetHeader:
                case TableGroup::WorksheetLine:
                    this.handleGlobalTransTable(sysDictTable);
                    break;
                default:
                    break;
            }
        }
    }
}

2) Add a new method to handle LEDGERJOURNALTABLE:
private void deleteLedgerJournalTables()
{
    GeneralJournalEntry         GJEntry;
    GeneralJournalAccountEntry  GJAEntry;
    LedgerJournalTable          ledgerjournalTable;
    LedgerEntryJournal          ledgerEntryJournal;

    ttsBegin;
    while select forupdate LedgerJournalTable
    {
        while select forUpdate ledgerEntryJournal
            where ledgerEntryJournal.JournalNumber == ledgerjournalTable.JournalNum
            //&&    ledgerEntryJournal.dataAreaId == ledgerjournalTable.dataAreaId
        {
            while select forUpdate GJEntry
                where GJEntry.LedgerEntryJournal == ledgerEntryJournal.RecId
            {
                  delete_from GJAEntry  where GJAEntry.GeneralJournalEntry == GJEntry.RecId;

                GJEntry.delete();
            }
            ledgerEntryJournal.delete();
        }
        LedgerJournalTable.delete();
    }
    ttsCommit;
}


3) Modify the 'handleTransTable()' method to call the above method
void handleTransTable(SysDictTable sysDictTable)
{
    switch(sysDictTable.id())
    {
        case tablenum(CustCollectionLetterLine):
        case tablenum(InventDim):
        case tablenum(DocuRef):
        case tablenum(DirPartyRelationship) :

            break;
        case tablenum(LedgerJournalTable) : 
            this.deleteLedgerJournalTables();
            break;

        default:
            this.deleteTable(sysDictTable);
            break;
    }
}

4) You may have to modify the 'deleteVoucher()' method in the 'LedgerJournalTrans' table to skip over releasing non-existing voucher numbers
public server void deleteVoucher(Voucher _voucher = this.Voucher)
{
    LedgerJournalTable  ledgerJournalTable = LedgerJournalTable::find(this.JournalNum);

    if (! ledgerJournalTable.Posted && !this.Transferred)
    {
        if (_voucher && ! LedgerJournalTrans::existTransMinusThis(this.JournalNum, _voucher, this.RecId))
        {
            if (this.checkVoucherNotUsed(ledgerJournalTable, _voucher))
            {
                if (this.checkVoucherNotUsedDataSource(_voucher))
                {
                    // replace the voucher number so it can be re-used
                    if (ledgerJournalTable.NumberSequenceTable) /* 28Nov12-Admin */
                        NumberSeq::releaseNumber(ledgerJournalTable.NumberSequenceTable, _voucher);

                    if (this.Voucher == _voucher)
                    {
                        // delete voucher template record if exists and the voucher on the line is not being changed
                        LedgerJournalTransVoucherTemplate::deleteForJournalOrVoucher(this.JournalNum, _voucher);
                    }
                }
            }
        }
    }
}
5) After running 'SysDatabaseTransDelete', rebuild balances for the financial dimension sets (General Ledger\Setup\Financial Dimensions\Financial dimension sets)

If you still have non-zero amounts in the Trial balance then you must manually remove the 'left-over' rows in the shared tables (results of your previous executions of the 'SysDatabaseTransDelete'). Identify these entries in the 'LedgerEntryJournal' table then use the following job to clear them:
static void tg_deleteTables(Args _args)
{
    GeneralJournalEntry         GJEntry;
    GeneralJournalAccountEntry  GJAEntry;
    LedgerJournalTable          ledgerjournalTable;
    LedgerEntryJournal          ledgerEntryJournal;

    ttsBegin;
        while select forUpdate ledgerEntryJournal
            where ledgerEntryJournal.JournalNumber like 'clau*'  
        {
            while select forUpdate GJEntry
                where GJEntry.LedgerEntryJournal == ledgerEntryJournal.RecId
            {
                  delete_from GJAEntry  where GJAEntry.GeneralJournalEntry == GJEntry.RecId;

                GJEntry.delete();
            }
            ledgerEntryJournal.delete();
        }

    ttsCommit;
    info('completed');
}



How to post PO by Code in Dynamics AX

I have task to integrate Dynamics AX with another application by posting PO from out side Dynamics AX, so I used AIF and build new web service to be used from out of AX and call the method below to post PO (packing slip, or invoice) .
public str CreatePostProductReceipt(PurchId _PurchId, Num _PackingSlip, ItemId  Itemid, Qty qty,
InventSiteId  InventSiteId ='', InventLocationId  InventLocationId= '' , inventBatchid batchid = '', InventSerialId  serialId = '', inventsizeId inventsizeId ='', InventColorId InventColorId ='')
{
PurchFormLetter             purchFormLetter;
PurchParmUpdate             purchParmUpdate;
PurchParmTable              purchParmTable;
PurchParmLine               purchParmLine;
PurchTable                  purchTable;
PurchLine                   purchLine;
PurchId                     purchId;
Num                         packingSlipId;
InventDim                   inventDim;
str                 ret='';
System.Exception    err;
;
packingSlipId   = _PackingSlip;
purchTable      = PurchTable::find(_PurchId);
ttsBegin;
try
{
// Create PurchParamUpdate table
purchFormletter = PurchFormLetter::construct(DocumentStatus::PackingSlip); // to post invoice change to DocumentStatus::invoice
purchFormLetter.createParmUpdate(true);
purchParmUpdate = PurchFormLetter.purchParmUpdate();
// Set PurchParmTable table
purchParmTable.clear();
purchParmTable.TransDate                = SystemDateGet();
purchParmTable.Ordering                 = DocumentStatus::PackingSlip;
purchParmTable.ParmJobStatus            = ParmJobStatus::Waiting;
purchParmTable.Num                      = packingSlipId;
purchParmTable.PurchId                  = purchTable.PurchId;
purchParmTable.PurchName                = purchTable.PurchName;
purchParmTable.DeliveryName             = purchTable.DeliveryName;
purchParmTable.OrderAccount             = purchTable.OrderAccount;
purchParmTable.CurrencyCode             = purchTable.CurrencyCode;
purchParmTable.InvoiceAccount           = purchTable.InvoiceAccount;
purchParmTable.ParmId                   = purchParmUpdate.ParmId;
purchParmTable.insert();
// Set PurchParmLine table
while select purchLine
where purchLine.PurchId == purchTable.purchId && purchline.ItemId == Itemid
{
purchParmLine.InitFromPurchLine(purchLine);
inventDim = purchline.inventDim(true);
// Set batch and serial number
if(InventSiteId != '')
inventDim.InventSiteId = InventSiteId;
if(InventLocationId != '')
inventDim.InventLocationId = InventLocationId;
if(batchid != '')
inventDim.inventBatchId = batchId;
if(serialid != '')
inventDim.inventSerialId = serialID;
if(inventsizeId != '')
inventDim.inventsizeId = inventsizeId;
if(InventColorId != '')
inventDim.InventColorId = InventColorId;
purchParmLine.InventDimId = inventDim::findOrCreate(inventdim).inventDimId;
purchParmLine.ReceiveNow    = 1 ; //PurchLine.PurchQty;
purchParmLine.setInventReceiveNow();
purchParmLine.ParmId        = purchParmTable.ParmId;
purchParmLine.TableRefId    = purchParmTable.TableRefId;
purchParmLine.setQty(DocumentStatus::PackingSlip, false, true);
purchParmLine.setLineAmount();
purchParmLine.insert();
}
ttsCommit;
purchFormLetter = PurchFormLetter::construct(DocumentStatus::PackingSlip);
purchFormLetter.transDate(systemDateGet());
purchFormLetter.proforma(false);
purchFormLetter.specQty(PurchUpdate::PackingSlip);
purchFormLetter.purchTable(purchTable);

// This is the ID we hard code as the product receipt ID, if we do the posting via UI
// user would have the option to manually enter this value
purchFormLetter.parmParmTableNum(purchParmTable.ParmId);
purchFormLetter.parmId(purchParmTable.ParmId);
purchFormLetter.purchParmUpdate(purchparmupdate);
purchFormLetter.run();
return "OK";
}
catch (Exception::CLRError)
{
err = CLRInterop::getLastException();
ret = err.ToString();
return ret;
}
Return "Error";
}

How to Call Report from Dynamics AX 2009

After you created your report in AX you can call report from  AX form by follow the below steps:

1- Create menu item with type output by drag and drop report to MenuItems nodes under AOT.

2- Open the form that you need to call report from.

                  Note: the form should be have dataSource with the table in report

3-  Drag and Drop menu Item created in step 1 in the form under any button group.

4- Add the following method to Report

void initFromCaller(Args _args)
{
    str                   QrderId; // field used as Rang and used to filter data
    QueryBuildDataSource  qbds; // should be represent report datasource and selected record in the form

    ;

    if (! _args ||
        ! _args.caller() ||
          _args.dataset() != tablenum(TableName) )
        return;

    QrderId = _args.record().(fieldnum(TableName,FieldName));

    qbds    =  element.query().dataSourceTable(tablenum(TableName));

    if(!qbds.findRange(fieldnum(TableName,FieldName)))
    {
        qbds.addRange(fieldnum(TableName,FieldName)) ;
    }
    qbds.findRange(fieldnum(TableName,FieldName)).value(queryvalue(QrderId));

}

How to reset TTSBegin/TTSCommit in AX

We are using TTSBegin and TTSCommit when update and create a record in AX, but some times an error is occurred  and through an exception.

If we did not abort TTS by using TTSAbort  statement between try and catch keyword as below

try

{

ttsBegin;

// your update code

ttsCommit;

}

catch

{

ttsAbort;

}

so we may facing this error below,

To fix this error please run the following job

static void ResetTTS(Args _args)
{
    while (appl.ttsLevel() > 0)
    {
        info(strfmt("Level %1 aborted",appl.ttsLevel()));
        ttsAbort;
    }
}

How to pass parameter to SSRS report from Dynamics AX

The below code for Passing parameters to SSRS using X++ code.

 

    MenuFunction SSRS_MyReport;

    Args Args;

    str parmId ="";

    ;

   // create reference to menu item “OverTime” which is in AOT
    SSRS_MyReport = new MenuFunction(menuItemOutputStr(OverTime),MenuItemType::Output);

    Args = new Args();

    // Set parameters and parameter value
    // I have 3 Parameters Nationality, EmplGroup, PeriodID

    parmId = "Nationality=KSA&EmplGroup=HERD&PeriodID=2014_05";

    // Assign parameters to report
    Args.parm(parmId);

    // Run the report
    SSRS_MyReport.run(Args);

 

 

Notes:

1- Parameters name is case sensitive, so it should set name as predefined in report.

2- You can pass more than one parameters by add “&”   between each parameter.

3- Not add space between parameters, it cause an error

Create and Post inventory journal by code : Dynamics AX

        InventJournalTable              inventJournalTable;
        InventJournalTrans              inventJournalTrans;

        InventJournalNameId             inventJournalName;
        InventDim                            inventDim;
        JournalCheckPost               journalCheckPost;
       

//Below code creates journal header       

        inventJournalTable.clear();

        inventJournalName =  InventJournalName::standardJournalName(InventJournalType::Movement);
        inventJournalTable.initFromInventJournalName(InventJournalName::find(inventJournalName ));

        inventJournalTable.insert();

       

//Below code creates journal lines

        inventJournalTrans.clear();

        inventJournalTrans.initFromInventJournalTable(inventJournalTable);

        inventJournalTrans.TransDate = systemDateGet();

        inventJournalTrans.ItemId = "MDJ0001";

        inventJournalTrans.initFromInventTable(InventTable::find("MDJ0001"));

        inventJournalTrans.Qty = 2500;

        inventDim.InventSiteId  = '12';

        inventDim.InventLocationId = '1201';

        inventDim.wMSLocationId = 'BULK-001';

        inventJournalTrans.InventDimId = inventDim::findOrCreate(inventDim).inventDimId;

        inventJournalTrans.insert();

       

//The below code posts the journal
        journalCheckPost = InventJournalCheckPost::newPostJournal(inventJournalTable);
        journalCheckPost.run();

How to: Create a Workflow Category [AX 2012]

When you create a workflow type in Microsoft Dynamics AX, it must be assigned to a workflow category. The workflow category determines whether a workflow type is available in a specific module. If an appropriate workflow category does not already exist, you must create one.

For example, a workflow type for a customer invoice should not be available in the Master planning module. To make the workflow type available only in the Customer module, create a workflow category with the Customer module selected.

The following procedure describes how to create a new workflow category.

To create a workflow category

  1. In the AOT, expand the Workflow node.
  2. Right-click the Workflow Categories node, and then click New Workflow Category. A new workflow category group displays under the Workflow Categories node.
  3. Right-click the new workflow category and then click Properties.
  4. In the Properties sheet, set the following properties as required.

Property

Value

Name

The name that is used to reference the workflow category.

Label

The label used for the workflow category in the user interface.

Help Text

The description of the workflow category displayed in the configuration user interface.

Module

The module that the workflow will be available in. The default is Ledger.

After a workflow category is created, you can bind the workflow type to the new category. Typically, this is performed by the Workflow Wizard. The following procedure describes how to manually bind a workflow type to a workflow category.

To bind a workflow type to a workflow category

  1. In the AOT, expand the Workflow node, and then expand the Workflow Types node.
  2. Right-click the workflow type that you want to bind a workflow category to, and then select Properties.
  3. In the Properties sheet, set the Category property to the workflow category created in the previous procedure.

How to: Create a New Module with Workflow [AX 2012]

In Microsoft Dynamics AX, modules are enabled for workflow. However, in some cases, you will need to create a new module that contains workflow. In each module that contains workflow, a Workflows list menu item must be added to the Setup pane. The following procedures are described in this topic:

  • Creating a new module enumeration.
  • Creating a display menu item for the Workflows list.
  • Creating a menu for the new module.
  • How to display the new menu in the client.

To Add a Module to the ModuleAxapta Base Enum

  1. In the Application Object Tree (AOT), expand the Data Dictionary node, expand Base Enums, right-click ModuleAxapta, and then click New Element. A new enumeration displays under the ModuleAxaptanode.
  2. Right-click the new enumeration, and then click Properties.
  3. In the Properties sheet, select the Label property, and then enter the label of the new module.

To Create a Display Menu Item for the Workflows List

  1. In the AOT, expand the Menu Items node.
  2. Right-click the Display node, and then click New Menu Item. A new menu item displays under the Display node.
  3. Right-click the new display menu item, and then click Properties.
  4. In the Properties sheet, set the following properties.

Property

Value

Name

Set to WorkflowConfigurations<xxx> where <xxx> is replaced by a reference to the name of the new module. For example, you could set this value to WorkflowConfigurationsRecruit for a new module named Recruiting.

Label

Set text or a label that represents the text for the workflows list in the client.

ObjectType

Set to Form.

Object

Set to WorkflowTableListPage.

EnumTypeParameter

Set to ModuleAxapta.

EnumParameter

Set to the enum created in the preceding procedure.

  1. In the AOT, right-click the new display menu item, and then click Save.

After the new module enum and display menu item are created, you can add them to the Menus node.

To Create a Menu for a New Module

  1. In the AOT, right-click the Menus node, and then click New Menu. A new menu displays under the Menus node.
  2. Right-click the new menu, and then click Properties.
  3. In the Properties sheet, set the following properties.

Property

Value

Name

Set to a label or name of the new module.

Label

Set to a label that represents the text to display for the new menu in the client.

  1. In the AOT, right-click the new menu, point to New, and then click New Submenu. A submenu node displays under menu node created in the previous step.
  2. Right-click the new submenu node, and then click Properties.
  3. In the Properties sheet, set the following properties.

Property

Value

Name

Set to Setup or another label that represents the text for the Setup pane in the client.

Label

Set to Setup or another label that represents the text for the Setup pane in the client.

NormalImage

Set to 3478. This will display a gears icon for the setup pane.

ImageLocation

Set to EmbeddedResource.

  1. In the AOT, right-click the Setup node created in the previous step, point to New, and then click New Menu item. A new menu item displays under the Setup node.
  2. Right-click the new menu item, and then click Properties.
  3. In the Properties sheet, set the MenuItemName property to the display menu item created in the previous procedure.
  4. In the AOT, right-click the new menu item, and then click Save.

After the menu for the new module is created, you must add a reference to the menu in the MainMenu node to display the menu in the client.

To Add a Menu to the Client

  1. In the AOT, expand the Menus node, right click MainMenus, point to New, and then click Menu reference. The Select: Menus window is displayed.
  2. In the Select: Menus window, select the new module menu that you created in the previous procedure, and drag the menu to the MainMenu node in the AOT.
  3. In the AOT, right-click the MainMenu node, and then click Save.

How to: Enable a State Model for a Workflow Document [AX 2012]

Workflow documents in a Microsoft Dynamics AX workflow must implement a state model to represent the state of a workflow, approval, or task for a workflow document in the application. For example, a workflow document in the NotSubmitted state can be changed to the Submitted state when a user clicks the Submit button. This section describes how to enable the state model for a workflow document by using a class or by creating a method on a table.

The following table contains recommended states. However, the needs of your application will determine what states are needed, validation type for state transitions, and how many states are needed. For more information, see Workflow Approval State Transitions post.

For the state of the Use
Workflow document
  • NotSubmitted

  • Submitted

  • ChangeRequested

  • Returned

  • Completed

Typically, the workflow state should be persisted in the root table of your workflow document data source. To add workflow state to a table, create an enum in the BaseEnums node of the Application Object Tree (AOT) that contains the workflow states shown in the previous table. The first enum defined should be the NotSubmitted enum, and this will also be the default setting in the table. After the enum is defined, drag the enum to the Fields node of the workflow document data source table.

After a state model is defined, you can enable forms and lists to work with workflow. For more information, see How to: Enable a Form or List for Workflow post.

NoteNote : Workflow state can be implemented in many ways. The following procedure shows one way of performing this task.

To create a state change manager class
  1. In the AOT, expand the Classes node.

  2. Right-click the Classes node, and then select New Class. A class group displays under the Classes node.

  3. Right-click the new class, click Rename, and then enter a name for the class.

  4. Right-click the new class and then click New Method. A method node named method1 displays under the Classes node.

  5. Right-click method1 and then click Edit. Enter the following code for the started event method.

    X++

    public static void started(RecID _recID)
    {
    <insert method here>
    }


  6. Repeat steps 4 and 5 for the remaining states.



Example



The following code is part of the setWorkflowState on the PurchReqTable Table. This method sets the workflow state field in the table and updates the user interface with a user friendly status, and then updates the state in the database.

X++

static void setWorkflowState(RecId _purchReqRecId, PurchReqWorkflowState _purchReqWorkflowState)
{
// Declare variables.
PurchReqTable purchReqTable;

ttsbegin;

// Selects the record to be updated based on the recId and then
// sets the workflow state.
purchReqTable = PurchReqTable::findRecId(_purchReqRecId, true);
purchReqTable.State = _purchReqWorkflowState;

// Based on the workflow state, the status in the user interface
// is updated with a user friendly status.
switch (_purchReqWorkflowState)
{
case PurchReqWorkflowState::Submitted:
purchReqTable.Status = PurchReqStatus::Submitted;
break;
case PurchReqWorkflowState::NotSubmitted:
purchReqTable.Status = PurchReqStatus::Draft;
break;
case PurchReqWorkflowState::Completed:
purchReqTable.Status = PurchReqStatus::Completed;
break;
case PurchReqWorkflowState::Returned:
purchReqTable.Status = PurchReqStatus::Rejected;
break;
case PurchReqWorkflowState::ChangeRequest:
purchReqTable.Status = PurchReqStatus::ChangeRequested;
break;
}

// Commits the state to the database.
purchReqTable.update();

ttscommit;
}

Workflow Approval State Transitions [AX 2012]

Microsoft Dynamics AX workflow documents that are enabled for workflow approvals typically support at least five workflow states. The current workflow state of a workflow can be maintained in a table field associated with the workflow document. As the state of the document is changed, the state field in the table should be updated. This topic describes workflow approval states.

Workflow State

Workflow state determines what you can do with the workflow document as soon as it is available in the system. Workflow states are defined by developers and must be supported in queries and business logic for operations that are controlled by workflow. Workflow approvals typically use the following workflow states.

 

Workflow state

Use

NotSubmitted

The state of the document before it is submitted to workflow or if the workflow is canceled. This is the state of all documents before a workflow instance is instantiated for the document.

Submitted

The state of the document as soon as the workflow is activated and the document is submitted to workflow. This is a temporary state when the user activates a workflow, but the system has not yet processed the workflow for activation.

PendingApproval

The state of the document as soon as the approval starts and remains in this state until the approval is completed, returned for a change, or rejected.

ChangeRequested

The state of the document if a change is requested or the document is returned.

Approved

The state of the document when the approval is completed.

The following diagram illustrates a typical approval state transition model.

Approval State Transition Model

Here are some general guidelines for workflow state:

  • Avoid creating your own workflow states unless you have a business justification for doing this.

  • Consider refactoring existing approval states so that you can take advantage of the states listed here.

Requirements for Enabling Workflow in an Application Module [AX 2012]

To enable a Microsoft Dynamics AX workflow for a new or existing application module, you must complete a series of required steps and then, depending on application requirements, complete additional steps. This topic describes the required steps to add workflow to your application.


Required Steps

The following steps are necessary for every workflow:

  1. Create a state model. For more information about a recommended state model, see Workflow Approval State Transitions and How to: Enable a State Model for a Workflow Document.

  2. Add a workflow display menu item. The workflow display menu item is used to access the workflow types for each module. For more information, see How to: Create a New Module with Workflow.

  3. Create a workflow category. A workflow category filters workflow types to a single module. If adding a new module, extend the ModuleAxapta Base Enums type. For more information, see How to: Create a Workflow Category.

  4. Create a workflow document class to define the workflow document for the workflow. You will create a query, and if needed, add calculated fields to identify the derived fields available for conditions displayed in the workflow configuration tool. For more information, see How to: Create a Workflow Document Class.

  5. Enable a form or list for workflow. You must set properties on a form or list to display the workflow button and controls. For more information, see How to: Enable a Form or List for Workflow.

  6. Enable documents to be submitted to the workflow. For more information, see How to: Enable Workflow Submission.

  7. Create a workflow type. A workflow type defines information about which workflow document to use, tasks, automated tasks, approvals, line item workflows, workflow category, menu items, and event handlers. For more information, see Creating a Workflow Type.

  8. Activate the workflow. There are several ways to activate the workflow. For more information, see Activating a Workflow.

  9. Define and implement event handlers for the workflow, approvals, tasks, and automated tasks. For more information, see Handling Workflow Events.

  10. Implement workflow providers for due date, participant, hierarchy, and queue assignment. For more information, see Workflow Providers.

  11. Define and implement approvals, tasks, and automated tasks. For more information, see Creating Workflow Tasks, Automated Tasks, and Approvals.

Optional Steps

  1. Create custom workflow providers. For more information, see How to: Create a Custom Workflow Provider.

  2. Create event handlers for tasks. For more information, see How to: Create a Workflow Event Handler.

Hint : You can find and follow All mentioned topics related to this post at my blog.

About Workflow Development [AX 2012]

Workflow is defined as the movement of documents or tasks through a work process. In Microsoft Dynamics AX, the focus of workflow is on approval and task-oriented workflows. The developer role in Microsoft Dynamics AX is primarily to add workflow to existing business documents or create new documents that support workflow. This topic describes what the workflow life cycle is and the developer role for a workflow in Microsoft Dynamics AX.

Workflow can be described as structured or unstructured. In Microsoft Dynamics AX, workflow is structured and based on user interaction and system automation of business data. For example, when a purchase requisition (business data) is created it, workflow can be used to verify and approve the data.

Workflow Lifecycle

All workflows follow a basic life cycle as shown in the following illustration.

Workflow Lifecycle

You design the workflow based on customer requirements. The company administrator configures the workflow, and users run the workflow. This section describes the some of the main development concepts used in the design section of the workflow life cycle in Microsoft Dynamics AX.

Workflow types

The workflow type is a building block that can be used to create customized workflows that enforce business policies. The workflow type is defined in the Application Object Tree (AOT) at design time. The metadata from the workflow type is used by the customer to create a workflow configuration.

Workflow configurations

Workflow configurations are created by application administrators that use the Microsoft Dynamics AX workflow editor. The administrator configures the workflow, workflow elements, and approval steps that control the flow of the business document though the workflow process.

Workflow instances

A workflow instance is created by the workflow runtime when a workflow is activated.

Workflow elements

The elements of a workflow are created by you in the AOT and configured by application administrators. The workflow structure consists of sequences of workflow elements. An element can be a task, automated task, approval, or a sub-workflow.

Approvals are specialized workflow elements that allow for sequencing of multiple steps, which use a fixed set of outcomes. Tasks are generic workflow elements that represent a single unit of work which use custom outcomes defined by the developer. Automated tasks are workflow elements used to invoke X++ code within the application without requiring human intervention.

Work items

Work items are the units of work created by the workflow at runtime. They are the main interface between the end user who participates in a workflow and the workflow runtime. All work items for users who are logged on are surfaced in the Unified Work List and are used to inform a user about work assignments.

Developer Role

You must create the workflow artifacts, dependent workflow artifacts, and business logic to support the workflow. The following sections describe most of the developer artifacts used in a Microsoft Dynamics AX workflow.

Workflow Artifacts

  • Workflow type

    • Define the workflow document.

    • Define event handlers for workflow Started, Completed, ConfigDataChanged, and Canceled.

    • Define menu items used for the workflow type like Submit.

    • Define the workflow category.

    • Define required approvals, tasks, and automated tasks.

    • Enable and disable activation conditions.

  • Workflow category

    • Define the module that the workflow type is enabled in.

  • Approval

    • Define the approval workflow document.

    • Define approval event handlers for Started and Canceled.

    • Define approval menu items such as Document, Resubmit, and Delegate.

    • Enable or disable fixed approval outcomes.

    • Define approval outcome menu items for Action and ActionWeb.

    • Define approval outcome event handler.

    • Define the DocumentPreviewFieldGroup.

  • Task

    • Define the task workflow document.

    • Define task event handlers for Started and Canceled.

    • Define task menu items for Document, DocumentWeb, Resubmit, ResubmitWeb, Delegate, and DelegateWeb.

    • Enable or disable task outcomes.

    • Define task outcome menu items for Action and ActionWeb.

    • Define task outcome event handler.

    • Define the DocumentPreviewFieldGroup.

  • Automated Task

    • Define the automated task workflow document.

    • Define automated task event handlers for Execution and Canceled.

Dependent Workflow Artifacts

The following workflow artifacts are dependent upon the type of workflow defined in the AOT.

  • Workflow Document class - identifies the document query and any calculated fields.

    • Document query - defined in the AOT to expose data that is used for conditions in the configuration user interface.

  • SubmitToWorkflow class - displays the Submit to Workflow dialog box in the user interface, receives user comments, activates the workflow, and can update workflow state.

  • State model - tracks the state of the document in the workflow process, for example, Submitted, ChangeRequested, or Approved.

  • Event handlers for the workflow itself on the workflow type, approval, approval outcomes, automated task, task, and task outcomes.

  • Action and display menu items as well as classes which determine the action taken when a menu item is selected in the user interface.

  • Custom workflow providers.

  • canSubmitToWorkflow method - required on each table enabled for workflow.

How to: Enable a Form or List for Workflow [AX 2012]

In Microsoft Dynamics AX, each form or list that is to be used by workflow must be enabled for workflow. This topic describes the steps to enable a form for workflow. It also describes the steps to enable a list for workflow.

When a form is enabled for workflow, the workflow menu bar is automatically inserted at the top of the form, below the form title bar or action pane. The workflow menu bar provides everything that you must have to interact with a workflow. The menu bar contains workflow action buttons, such as Submit and Complete, and data fields that contain workflow instructions, configuration name, and information icons.

To display the Submit button, the form must have a canSubmitToWorkflow method.

Note: The form data source, or table, must contain a field for the workflow state of the document. For example, a field that contains the workflow state NotSubmitted should be updated to Submitted when the user clicks the Submit button on the form.

To enable a form for workflow
  1. In the Application Object Tree (AOT), expand the Forms node.

  2. Expand the form that you want to enable for workflow, and then expand the Designs node.

  3. In the Designs node, right-click the Design child node, and then click Properties.

  4. In the Properties sheet, set the following properties.

    Property Value
    WorkflowEnabled Set to Yes to enable the workflow menu bar on the form. The default setting is No.
    WorkflowDataSource Set to the same root data source specified in the query used for the Document property on the workflow type.
    WorkflowType Set to the workflow type that you want to use for the list.
  5. In the AOT, right-click the form, and then click Save.

To enable a list for workflow
  1. In the Application Object Tree (AOT), expand the Forms node.

  2. Expand the form for the list that you want to enable for workflow, and then expand the Designs node.

  3. In the Designs node, right-click the Design child node, and then click Properties.

  4. In the Properties sheet, set the following properties.   
    Property Value
    WorkflowEnabled Set to Yes to enable the workflow menu bar on the list. The default setting is No.
    WorkflowDataSource Set to the same root data source specified in the query used for the Document property on the workflow type.
    WorkflowType Set to the workflow type that you want to use for the list.

   5.    In the AOT, right-click the List, and then click Save.

Sleep Function – Dynamics AX

 

Pauses the execution of the current thread for the specified number of milliseconds.

static void sleepExample(Args _arg)
{
int seconds = 10; // number of second to sleep
int i;
;
i = sleep(seconds*1000);
print "job slept for " + int2str(i/1000) + " seconds";
pause;

}

Walkthrough: Adding an X++ Object to a Visual Studio Project [AX 2012]

This walkthrough illustrates the following tasks:

  • Creating the class library, adding a Microsoft Dynamics AX table to the project, and accessing that table from code.

  • Creating a console application project and testing the generated assembly.

TipTip: Although this topic shows you how to test the generated assembly from Visual Studio, you can also test it from X++ because after you add the project to the AOT, the classes in the assembly are available from Microsoft Dynamics AX.

Prerequisites

To complete this walkthrough you will need:

  • Microsoft Dynamics AX with Visual Studio Tools and sample data installed

  • Visual Studio 2010

Creating the Class Library

The first step is to create the class library in Visual Studio. After you have created the class library project, you can then add a Microsoft Dynamics AX table to the project. Then you can create class methods that access that table.

To create the class library
  1. To create a new class library project, click File > New> Project.

  2. Below the Installed Templates tree, click Visual C# > Windows, select the Class Library project type and then click OK.

  3. Save the new project.

  4. Add the project to the AOT by clicking File > Add ClassLibrary1 to AOT. Notice that the project icon changes in Solution Explorer. Alternatively, you can right-click the ClassLibrary1 project and select Add ClassLibrary1 to AOT.

To add a Microsoft Dynamics AX table to the project
  1. Open the Application Explorer by clicking View > Application Explorer. Expand the Data Dictionary > Tables node and locate the CustTable table.

  2. Click the CustTable table and drag it onto the project in Solution Explorer. Alternatively, you can right-click the table and then click Add to Project. In Solution Explorer, you can see that the table and a proxy to the CustTable table are created internally by the system. In the References node you can see a reference to the assembly Microsoft.Dynamics.Ax.ManagedInterop.

    TipTip: You can add a system table or system class to your Visual Studio project even if the  interface does not support the dragging of system objects. For example, to add the FormRun system class, first add any application class, such as the Bank class. Then rename the new proxy node from Class.Bank.axproxy to Class.FormRun.axproxy.

To create methods that use the table
  • Open the Class1.cs file and add the following code. This code contains two methods that each take two parameters and return data for the specified customer.

    C#

    using System;

    namespace ClassLibrary1
    {
    public class Class1
    {
    public string GetCustomerPaymentMode(string accountNum, string dataAreaId)
    {

    string paymentMode = String.Empty;
    CustTable custTable = new CustTable();

    // Search for the customer.
    custTable = CustTable.findByCompany(dataAreaId, accountNum);

    if (custTable.Found)
    {
    // Get the value for the customer's payment mode.
    paymentMode = custTable.PaymMode;
    }

    return paymentMode;
    }

    public bool GetCustomerCreditLimit(string accountNum, string dataAreaId)
    {

    bool hasCreditLimit = false;
    CustTable custTable = new CustTable();

    // Search for the customer.
    custTable = CustTable.findByCompany(dataAreaId, accountNum);

    if (custTable.Found)
    {
    // Get the value for whether the customer has a credit limit.
    hasCreditLimit = (custTable.MandatoryCreditLimit == NoYes.No ? false : true);
    }

    return hasCreditLimit;
    }
    }
    }

Creating a Console Application Project



To create a console application project to test the assembly



  1. In Solution Explorer, right-click the solution ClassLibrary1 and select Add > New Project.



  2. Below the Installed Templates tree, click Visual C# > Windows, select the Console Application project type and then click OK.



  3. In Solution Explorer, add a reference to the ClassLibrary1 project by right-clicking the References node under the ConsoleApplication1 project and then clicking Add Reference.



  4. Click the Projects tab, select the ClassLibrary1 project and then click OK.



  5. In Solution Explorer, add a reference to the managed interop assembly by right-clicking the References node under the ConsoleApplication1 project and then clicking Add Reference.



  6. Click the Browse tab, locate the Microsoft.Dynamics.AX.ManagedInterop assembly and select OK. This assembly is located in the Client\Bin directory. For example, C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin. This reference is necessary to use the Session object.


To call methods to display output to the console application



  1. Open the Program.cs file and add the following code. This code calls the GetCustomerPaymentMode and the GetCustomerCreditLimit methods from the class you created and displays the values in the console window.


    C#

    using System;
    using ClassLibrary1;
    using Microsoft.Dynamics.AX.ManagedInterop;

    namespace ConsoleApplication1
    {
    class Program
    {
    static void Main(string[] args)
    {
    // Create a session
    using (Session session = new Session())
    {
    session.Logon(null, null, null, null);

    Class1 class1 = new Class1();
    string accountNum = "3003";
    string dataAreaId = "ceu";
    string paymentMode = String.Empty;
    string hasCreditLimitText = String.Empty;
    bool hasCreditLimit;

    // Get the customer payment mode and credit limit.
    paymentMode = class1.GetCustomerPaymentMode(accountNum, dataAreaId);
    hasCreditLimit = class1.GetCustomerCreditLimit(accountNum, dataAreaId);
    hasCreditLimitText = (hasCreditLimit == false ? " and does not have" : " and has");

    // Write the data to the console
    Console.WriteLine("Customer " + accountNum + " in company " + dataAreaId +
    " has a payment mode of " + paymentMode + hasCreditLimitText +
    " a mandatory credit limit." );
    Console.ReadLine();
    }

    }
    }
    }


  2. In Solution Explorer, set the console application to be the startup project by right-clicking ConsoleApplication1 and then clicking Set as StartUp Project.


To test the generated assembly



  • Run the application. If you have the sample data installed, you will see the following output in the console window:


    Customer 3003 in company ceu has a payment mode of CHCK and does not have a mandatory credit limit.


    TipTip: You may receive an error about the .NET target framework because both projects target the .NET Framework 4 by default but the Microsoft.Dynamics.AX.ManagedInterop assembly is a mixed-mode assembly that targets the 2.0 runtime.


    To resolve this error you have two options: you can change the target framework for both projects to .NET Framework 3.5 or you can add an attribute to the app.config file of the console application project. To change the target framework, right-click each project in Solution Explorer and then click Properties. In the Target framework field, select .NET Framework 3.5.


    If you want the console application project to target the .NET Framework 4, add the useLegacyV2RuntimeActivationPolicy attribute to the startup element in the app.config file and set it to true. For example, <startup useLegacyV2RuntimeActivationPolicy="true">.

How to create an item using ItemServices and EcoResProductServices in AX 2012

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.ServiceModel.Description;

using System.Text;

using Tutorial.AIF.CreateItem.EcoResProductServices;

using Tutorial.AIF.CreateItem.InventItemServices;

 

namespace Tutorial.AIF.CreateItem

{

class Program

{

static void Main(string[] args)

{

try

{

ItemServiceCreateRequest request = new ItemServiceCreateRequest();

ItemServiceClient client = new ItemServiceClient();

 

EcoResProductServiceCreateRequest prodRequest = new EcoResProductServiceCreateRequest();

EcoResProductServiceClient prodClient = new EcoResProductServiceClient();

 

ReqItemTableServiceCreateRequest reqItemRequest = new ReqItemTableServiceCreateRequest();

ReqItemTableServiceClient reqItemClient = new ReqItemTableServiceClient();

 

reqItemRequest.CallContext = new InventItemServices.CallContext();

reqItemRequest.CallContext.Company = "CAD";

reqItemRequest.CallContext.Language = "en-us";

reqItemRequest.CallContext.MessageId = Guid.NewGuid().ToString();

 

prodRequest.CallContext = new EcoResProductServices.CallContext();

prodRequest.CallContext.Language = "en-us";

prodRequest.CallContext.Company = "CAD";

prodRequest.CallContext.MessageId = Guid.NewGuid().ToString();

request.CallContext = new Tutorial.AIF.CreateItem.InventItemServices.CallContext();

request.CallContext.Language = "en-us";

request.CallContext.Company = "CAD";

request.CallContext.MessageId = Guid.NewGuid().ToString();

 

AxdEntity_Product_EcoResProduct[] ecoResProduct = new AxdEntity_Product_EcoResProduct[1];

ecoResProduct[0] = new AxdEntity_Product_EcoResDistinctProduct();

ecoResProduct[0].ProductType = AxdEnum_EcoResProductType.Item;

ecoResProduct[0].SearchName = "68727900121";

ecoResProduct[0].DisplayProductNumber = "ITEM002";

 

AxdEntity_Translation prodTranslation = new AxdEntity_Translation();

prodTranslation.Name = "ITEM002";

prodTranslation.LanguageId = "en-us";

 

ecoResProduct[0].Translation = new[] { prodTranslation };

 

 

AxdEntity_Identifier identifier = new AxdEntity_Identifier();

identifier.ProductNumber = "68727900121";

 

ecoResProduct[0].Identifier = new AxdEntity_Identifier[1];

ecoResProduct[0].Identifier[0] = identifier;

 

 

AxdEcoResProduct product = new AxdEcoResProduct();

product.Product = ecoResProduct;

 

prodClient.Open();

Tutorial.AIF.CreateItem.EcoResProductServices.EntityKey[] keys = prodClient.create(prodRequest.CallContext, product);

prodClient.Close();

 

AxdEntity_InventTable[] inventTable = new AxdEntity_InventTable[1];

inventTable[0] = new AxdEntity_InventTable();

inventTable[0].ItemId = "ITEM002";

inventTable[0].NameAlias = "ITEMNAMEALIAS";

inventTable[0].Product = "ITEM002";

//inventTable[0].Product = keys[0].KeyData[0].Value.Trim();

 

inventTable[0].StorageDimensionGroup = new[] { new AxdEntity_StorageDimensionGroup { StorageDimensionGroup = "DEF", ItemId = inventTable[0].ItemId } };

inventTable[0].TrackingDimensionGroup = new[] { new AxdEntity_TrackingDimensionGroup { TrackingDimensionGroup = "DEF", ItemId = inventTable[0].ItemId } };

inventTable[0].InventModelGroupItem = new[] { new AxdEntity_InventModelGroupItem { ModelGroupId = "DEF", ItemId = inventTable[0].ItemId } };

inventTable[0].InventItemGroupItem = new[] { new AxdEntity_InventItemGroupItem { ItemGroupId = "ALL", ItemId = inventTable[0].ItemId } };

 

//if you want to insert reqItemTable (item coverage settings) you need to create a service for that

//INVENTDIMID

inventTable[0].InventItemPurchSetup = new[] {

new AxdEntity_InventItemPurchSetup {

//HERE WE WILL ADD ORDER SPECIFIC SETTINGS FOR THIS PARTICULAR ITEM

ItemId = inventTable[0].ItemId,

InventDimPurchSetup = new AxdEntity_InventDimPurchSetup[] {

new AxdEntity_InventDimPurchSetup() {

InventDimId = "AllBlank"

}

},

DefaultInventDimPurchSetup = new [] {

new AxdEntity_DefaultInventDimPurchSetup() {

InventDimId = "AllBlank",

InventSiteId = "MTL-01"

}

}

},

//HERE WE WILL ADD SITE SPECIFIC SETTINGS FOR THIS PARTICULAR ITEM

new AxdEntity_InventItemPurchSetup {

ItemId = inventTable[0].ItemId,

InventDimPurchSetup = new AxdEntity_InventDimPurchSetup[] {

new AxdEntity_InventDimPurchSetup() {

InventSiteId = "MTL-01"

}

},

DefaultInventDimPurchSetup = new [] {

new AxdEntity_DefaultInventDimPurchSetup() {

InventLocationId = "01"

}

}

},

};

 

 

inventTable[0].InventItemSalesSetup = new[] {

new AxdEntity_InventItemSalesSetup {

ItemId = inventTable[0].ItemId,

InventDimSalesSetup = new AxdEntity_InventDimSalesSetup[] {

new AxdEntity_InventDimSalesSetup() {

InventDimId = "AllBlank"

}

},

DefaultInventDimSalesSetup = new [] {

new AxdEntity_DefaultInventDimSalesSetup() {

InventDimId = "AllBlank",

InventSiteId = "MTL-01"

}

}

 

},

new AxdEntity_InventItemSalesSetup {

ItemId = inventTable[0].ItemId,

InventDimSalesSetup = new AxdEntity_InventDimSalesSetup[] {

new AxdEntity_InventDimSalesSetup() {

InventSiteId = "MTL-01"

}

},

DefaultInventDimSalesSetup = new [] {

new AxdEntity_DefaultInventDimSalesSetup() {

InventLocationId = "01"

}

}

 

}

};

 

inventTable[0].InventItemInventSetup = new[] {

new AxdEntity_InventItemInventSetup {

ItemId = inventTable[0].ItemId,

InventDimInventSetup = new AxdEntity_InventDimInventSetup[] {

new AxdEntity_InventDimInventSetup() {

InventDimId = "AllBlank"

}

},

DefaultInventDimInventSetup = new [] {

new AxdEntity_DefaultInventDimInventSetup() {

InventDimId = "AllBlank",

InventSiteId = "MTL-01"

}

}

 

},

new AxdEntity_InventItemInventSetup {

ItemId = inventTable[0].ItemId,

InventDimInventSetup = new AxdEntity_InventDimInventSetup[] {

new AxdEntity_InventDimInventSetup() {

InventSiteId = "MTL-01"

}

},

DefaultInventDimInventSetup = new [] {

new AxdEntity_DefaultInventDimInventSetup() {

InventLocationId = "01"

}

}

 

}

};

 

 

AxdItem items = new AxdItem();

items.InventTable = inventTable;

 

client.Open();

client.create(request.CallContext, items);

client.Close();

}

catch (Exception e)

{

Console.WriteLine(e.Message);

Console.Read();

}

 

 

}

}

}

 

Imparted from here