2013年4月11日星期四

Dynamics AX 2012 Products Import using Excel Add-in

http://ax2012exceldataimport.blogspot.com/2012/01/dynamics-ax-2012-products-import-using.html

Dynamics AX 2012 SSRS reports patterns

Here are the design pattern/changes that we need to make sure for AX 2012 SSRS report development:


Functional Area
AX2009
AX2012
Input parameters (definition)
Report
Data contract (*)
Input parameters (validation)
Report
Data contract (*)
Input dialog (simple)
Report
Data contract (*)
Input dialog (complex)
Report
UI Builder (*)
Input parameters (modification)
Report
Controller (*)
Dynamic query
Report
Query
Report data
Report
Table (temp)
Business logic
Report
RDP
Report layout
Report
SSRS Report

Open Dynamics ax in specific language

 

To open MS Dynamics AX in specific language via a command, you can use it in following manner:

1. Open commoand prompt.
2. Type Ax32.exe –language=culture_id

Example : Ax32.exe –language=en-us

How to using x++ release products to the legal entities


In this post, I would like to share X++ Code that will help to release products to the legal entities from product master.

static void THK_releaseProductsWithJob(Args _args)
{
    EcoResProduct ecoResProduct;
    InventTable inventTable;
    InventTableModule inventTableModule;
    NumberSequenceTable numberSequenceTable;
    ItemId itemId;
    InventItemSetupSupplyType inventItemSetupSupplyType;

    EcoResStorageDimensionGroupProduct ecoResStorageDimensionGroupProduct;
    EcoResTrackingDimensionGroupProduct ecoResTrackingDimensionGroupProduct;
    EcoResStorageDimensionGroupItem ecoResStorageDimensionGroupItem;
    EcoResTrackingDimensionGroupItem ecoResTrackingDimensionGroupItem;
    ;

    select firstOnly ecoResProduct where EcoResProduct.DisplayProductNumber == "test"; //Audio system
    changecompany ('CEU')
    {
        ttsBegin;
        inventTable = null;
        inventTableModule = null;
        inventItemSetupSupplyType = null;
        ecoResStorageDimensionGroupProduct = null;
        ecoResTrackingDimensionGroupProduct = null;
        ecoResStorageDimensionGroupItem = null;
        ecoResTrackingDimensionGroupItem = null;

        numberSequenceTable = InventParameters::numRefItemId().numberSequenceTable();
        //get item id from:
        //1. Product number if number seq for item ID is not set up or manual or return blank value
        if (!numberSequenceTable.RecId || numberSequenceTable.Manual)
        {
            itemId = ecoResProduct.productNumber();
        }
        else //number sequence auto, get a number
        {
            itemId = NumberSeq::newGetNumFromId(numberSequenceTable.RecId).num();
        }

        inventTable.initValue();
        inventTable.initFromEcoResProduct(ecoResProduct);
        inventTable.ItemId = ItemId;
        inventTable.NameAlias = ecoResProduct.SearchName;
        inventTable.insert(true);

        // Create inventTableModules
        inventTableModule.initValue();
        inventTableModule.ItemId = inventTable.ItemId;
        inventTableModule.ModuleType = ModuleInventPurchSales::Invent;
        inventTableModule.insert();

        inventTableModule.initValue();
        inventTableModule.ItemId = inventTable.ItemId;
        inventTableModule.ModuleType = ModuleInventPurchSales::Purch;
        inventTableModule.insert();

        inventTableModule.initValue();
        inventTableModule.ItemId = inventTable.ItemId;
        inventTableModule.ModuleType = ModuleInventPurchSales::Sales;
        inventTableModule.insert();

        //Create inventItemLocation
        InventItemLocation::createDefault(inventTable.ItemId);

        // Creates a new item default order type for the product that is released.
        inventItemSetupSupplyType.initValue();
        inventItemSetupSupplyType.ItemId = inventTable.ItemId;
        inventItemSetupSupplyType.ItemDataAreaId = inventTable.DataAreaId;
        inventItemSetupSupplyType.insert();

        //create relationship tables to dimension groups.
        ecoResStorageDimensionGroupProduct = EcoResStorageDimensionGroupProduct::findByProduct(ecoResProduct.RecId);
        ecoResTrackingDimensionGroupProduct = EcoResTrackingDimensionGroupProduct::findByProduct(ecoResProduct.RecId);

        if (ecoResStorageDimensionGroupProduct.RecId)
        {  
            // mandatory storage dimension group for the product
            ecoResStorageDimensionGroupItem.ItemDataAreaId = inventTable.DataAreaId;
            ecoResStorageDimensionGroupItem.ItemId = inventTable.ItemId;
            ecoResStorageDimensionGroupItem.StorageDimensionGroup = ecoResStorageDimensionGroupProduct.StorageDimensionGroup;
            ecoResStorageDimensionGroupItem.insert();
        }

        if (ecoResTrackingDimensionGroupProduct.RecId)
        {
            // mandatory tracking dimension group for the product
            ecoResTrackingDimensionGroupItem.ItemDataAreaId = inventTable.DataAreaId;
            ecoResTrackingDimensionGroupItem.ItemId = inventTable.ItemId;
            ecoResTrackingDimensionGroupItem.TrackingDimensionGroup = ecoResTrackingDimensionGroupProduct.TrackingDimensionGroup;
            ecoResTrackingDimensionGroupItem.insert();
        }
       
        ttsCommit;
       
        info("Product successfully released to CEU legal entity");
    }
}

2013年4月10日星期三

How to using .net parsing Date Strings

Dynamics AX provides functions for parsing date strings into a date object but they are limited at best.
The following static method uses the .Net date parsing functionality to create a X++ date object from any valid date string without knowing the year, month, and day sequence or anything else about it for that matter.
It also allows you to specify the value that is returned in case of an invalid string being passed.

static date ParseDateString(str _dateValue, date _default = datenull())
{
    date    res;
    int     infoCnt;
;
    new InteropPermission(InteropKind::ClrInterop).assert();
    try
    {
        infoCnt = infolog.line();
        res = System.DateTime::Parse(_dateValue);
    }
    catch
    {
        res = _default;
        infolog.clear(infoCnt);
    }
    CodeAccessPermission::revertAssert();

    return res;
}

How to transfer CLR Array to AX List

If you use any amount of CLRInterop programming in Dynamics AX you have probably at some point or another needed to use X++ code to interact with an array object passed back from a bit of CLR code.
I created the following static class to ease the process of converting a CLRArray object into a X++ style list object.
It only requires two parameters to be passed for the conversion:
  1. CLRObject _input – this is the object array you need converted.
  2. Types _type – this is the X++ type that you want the individual CLR objects converted to.
static List CLRArrayToList(CLRObject _input, Types _type)
{
    System.Collections.ArrayList list;

    List l = new List(_type);
    AnyType x;

    int cnt, i;
;
    new InteropPermission(InteropKind::ClrInterop).assert();
    try
    {
        list = new CLRObject('System.Collections.ArrayList',_input);

        cnt = list.get_Count();

        for(i = 0; i < cnt;i++)
        {
            x = CLRInterop::getAnyTypeForObject(list.get_Item(i));
            l.addEnd(x);
        }
    }
    catch
    {
        l = new List(_type);
    }
    CodeAccessPermission::revertAssert();

    return l;
}
To utilize the method add it to a class (we will name it QuickTools for this example) and call it like this:
CLRObject   bytes;
List        intList = new List(Types::Integer);

// Some code here to populate the bytes object with numeric values
intList = QuickTools::CLRArrayToList(bytes,Types::Integer);
// intList now contains the values stored in bytes
Using this example and a bit of creativity you could create a static method to perform the opposite action and convert a X++ list into a CLR Array object

2013年3月21日星期四

How to updated the caller Form/DataSource

If we need to notify events to the caller form we can try this pattern:

In the child form, make a method named updateCaller and invoke it when you want to notify the parent:

void updateCaller()
{
    Common common;
    Object dataSource;
    Object caller;
    ;

    //-----------------------------------
    //We are notifying using the dataSource
    common = element.args().record();
    if (common
        && common.isFormDataSource()
        && formDataSourceHasMethod(common.dataSource(), identifierstr(SomethingWasHappend)))
    {
        dataSource = common.dataSource();
        dataSource.SomethingWasHappend();
    }
    //-----------------------------------

    //-----------------------------------
    //We are notifying using the form
    caller = element.args().caller();
    if (caller
        && classidget(caller) == classnum(SysSetupFormRun)
        && formHasMethod(caller, identifierstr(SomethingWasHappend)))
    {
        caller.SomethingWasHappend();
    }
    //-----------------------------------
}

Implement the handling method in the parent form:

void SomethingWasHappend()
{
    ;
    info("Something was happend (Form)");
}

Or if you prefer, in the DataSource:

void SomethingWasHappend()
{
    ;
    info("Something was happend (DataSource)");
}

Invoking it from a simple button inside the child form:

void clicked()
{
    super();

    element.updateCaller();
}


See a pratical sample in the form MarkupTrans (DataSource -> method write).